packet_receive.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2001 Pekka Riikonen
+ Copyright (C) 1997 - 2002 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "serverincludes.h"
#include "server_internal.h"
-extern char *server_version;
-
/* Received notify packet. Server can receive notify packets from router.
Server then relays the notify messages to clients if needed. */
SilcChannelID *channel_id = NULL, *channel_id2;
SilcClientID *client_id, *client_id2;
SilcServerID *server_id;
- SilcChannelEntry channel;
- SilcClientEntry client;
- SilcServerEntry server_entry;
+ SilcIdType id_type;
+ SilcChannelEntry channel = NULL;
+ SilcClientEntry client = NULL, client2 = NULL;
+ SilcServerEntry server_entry = NULL;
SilcChannelClientEntry chl;
SilcIDCacheEntry cache;
SilcHashTableList htl;
- uint32 mode;
+ SilcUInt32 mode;
unsigned char *tmp;
- uint32 tmp_len;
+ SilcUInt32 tmp_len;
+ bool local;
SILC_LOG_DEBUG(("Start"));
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, NULL, &idata);
+ packet->dst_id_len, NULL,
+ &idata, NULL);
if (dst_sock)
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac_receive, packet, TRUE);
+ idata->hmac_send, idata->psn_send++,
+ packet, TRUE);
}
/* Parse the Notify Payload */
- payload = silc_notify_payload_parse(packet->buffer);
+ payload = silc_notify_payload_parse(packet->buffer->data,
+ packet->buffer->len);
if (!payload)
return;
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
if (!tmp)
goto out;
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!channel_id)
goto out;
tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!client_id)
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);
+ sock->user_data, NULL, 0);
if (!client) {
SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
silc_free(client_id);
break;
/* Do not add client to channel if it is there already */
- if (silc_server_client_on_channel(client, channel)) {
+ if (silc_server_client_on_channel(client, channel, NULL)) {
SILC_LOG_DEBUG(("Client already on channel"));
break;
}
/* The channel is global now */
channel->global_users = TRUE;
+ SILC_LOG_DEBUG(("Joining to channel %s", channel->channel_name));
+
/* 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));
silc_hash_table_add(channel->user_list, client, chl);
silc_hash_table_add(client->channels, channel, chl);
silc_free(client_id);
+ channel->user_count++;
break;
silc_free(channel_id);
goto out;
}
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!client_id) {
silc_free(channel_id);
goto out;
silc_free(client_id);
/* Check if on channel */
- if (!silc_server_client_on_channel(client, channel))
+ if (!silc_server_client_on_channel(client, channel, NULL))
break;
/* Send the leave notify to channel */
tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!client_id)
goto out;
if (tmp_len > 128)
tmp = NULL;
+ /* Update statistics */
+ server->stat.clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
/* Remove the client from all channels. */
silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
- server->stat.clients--;
- if (server->server_type == SILC_ROUTER)
- server->stat.cell_clients--;
break;
case SILC_NOTIFY_TYPE_TOPIC_SET:
SILC_LOG_DEBUG(("TOPIC SET notify"));
+ /* 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, NULL);
+ if (!client_id)
+ goto out;
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
+
+ /* Get the topic */
+ tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+ if (!tmp) {
+ silc_free(channel_id);
+ goto out;
+ }
+
if (!channel_id) {
channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
packet->dst_id_type);
}
}
- /* Get the topic */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp) {
- silc_free(channel_id);
+ if (channel->topic && !strcmp(channel->topic, tmp))
+ goto out;
+
+ /* Get user's channel entry and check that topic set is allowed. */
+ if (!silc_server_client_on_channel(client, channel, &chl))
+ goto out;
+ if (chl->mode == SILC_CHANNEL_UMODE_NONE &&
+ channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+ SILC_LOG_DEBUG(("Topic change is not allowed"));
goto out;
}
- if (channel->topic)
- silc_free(channel->topic);
- channel->topic = silc_calloc(tmp_len + 1, sizeof(*channel->topic));
- memcpy(channel->topic, tmp, tmp_len);
+ /* Change the topic */
+ silc_free(channel->topic);
+ channel->topic = strdup(tmp);
/* Send the same notify to the channel */
silc_server_packet_send_to_channel(server, sock, channel, packet->type,
* Distribute the notify to local clients on the channel
*/
unsigned char *id, *id2;
+ char *nickname;
+ SilcUInt32 nickname_len;
SILC_LOG_DEBUG(("NICK CHANGE notify"));
id = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!id)
goto out;
- client_id = silc_id_payload_parse_id(id, tmp_len);
+ client_id = silc_id_payload_parse_id(id, tmp_len, NULL);
if (!client_id)
goto out;
id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
if (!id2)
goto out;
- client_id2 = silc_id_payload_parse_id(id2, tmp_len);
+ client_id2 = silc_id_payload_parse_id(id2, tmp_len, NULL);
if (!client_id2)
goto out;
SILC_LOG_DEBUG(("New Client ID id(%s)",
silc_id_render(client_id2, SILC_ID_CLIENT)));
+ /* From protocol version 1.1 we also get the new nickname */
+ nickname = silc_argument_get_arg_type(args, 3, &nickname_len);;
+
/* Replace the Client ID */
client = silc_idlist_replace_client_id(server->global_list, client_id,
- client_id2);
+ client_id2, nickname);
if (!client)
client = silc_idlist_replace_client_id(server->local_list, client_id,
- client_id2);
+ client_id2, nickname);
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)
- silc_free(client->nickname);
- client->nickname = NULL;
-
/* Send the NICK_CHANGE notify type to local clients on the channels
this client is joined to. */
- silc_server_send_notify_on_channels(server, NULL, client,
- SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
- id, tmp_len,
- id2, tmp_len);
+ silc_server_send_notify_on_channels(server, client, client,
+ SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+ id, tmp_len, id2, tmp_len,
+ nickname, nickname ?
+ nickname_len : 0);
}
silc_free(client_id);
SILC_LOG_DEBUG(("CMODE CHANGE notify"));
+ /* 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, &id_type);
+ if (!client_id)
+ goto out;
+
+ /* Get client entry */
+ if (id_type == SILC_ID_CLIENT) {
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
+ }
+
if (!channel_id) {
channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
packet->dst_id_type);
goto out;
}
}
+ silc_free(channel_id);
/* Get the mode */
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (!tmp) {
- silc_free(channel_id);
+ if (!tmp)
goto out;
- }
-
SILC_GET32_MSB(mode, tmp);
/* Check if mode changed */
if (channel->mode == mode)
break;
+ /* Get user's channel entry and check that mode change is allowed */
+ if (client) {
+ if (!silc_server_client_on_channel(client, channel, &chl))
+ goto out;
+ if (!silc_server_check_cmode_rights(server, channel, chl, mode)) {
+ SILC_LOG_DEBUG(("CMODE change is not allowed"));
+ goto out;
+ }
+ }
+
/* Send the same notify to the channel */
silc_server_packet_send_to_channel(server, sock, channel, packet->type,
FALSE, packet->buffer->data,
/* Change mode */
channel->mode = mode;
- silc_free(channel_id);
/* Get the hmac */
tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
/* Set the HMAC key out of current channel key. The client must do
this locally. */
- silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+ channel->key_len / 8,
hash);
silc_hmac_set_key(channel->hmac, hash,
- silc_hash_len(channel->hmac->hash));
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
memset(hash, 0, sizeof(hash));
}
+ /* Get the passphrase */
+ tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+ if (tmp) {
+ silc_free(channel->passphrase);
+ channel->passphrase = silc_memdup(tmp, tmp_len);
+ }
+
break;
case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
+ /* 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, &id_type);
+ if (!client_id)
+ goto out;
+
+ /* Get client entry */
+ if (id_type == SILC_ID_CLIENT) {
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
+ }
+
if (!channel_id) {
channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
packet->dst_id_type);
tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
if (!tmp)
goto out;
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!client_id)
goto out;
/* Get client entry */
- client = silc_idlist_find_client_by_id(server->global_list,
- client_id, TRUE, NULL);
- if (!client) {
- client = silc_idlist_find_client_by_id(server->local_list,
- client_id, TRUE, NULL);
- if (!client) {
+ client2 = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, NULL);
+ if (!client2) {
+ client2 = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!client2) {
silc_free(client_id);
goto out;
}
}
silc_free(client_id);
+ if (client) {
+ /* Check that sender is on channel */
+ if (!silc_server_client_on_channel(client, channel, &chl))
+ goto out;
+
+ if (client != client2) {
+ /* Sender must be operator */
+ if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+ SILC_LOG_DEBUG(("CUMODE change is not allowed"));
+ goto out;
+ }
+
+ /* Check that target is on channel */
+ if (!silc_server_client_on_channel(client2, channel, &chl))
+ goto out;
+
+ /* If target is founder mode change is not allowed. */
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ SILC_LOG_DEBUG(("CUMODE change is not allowed"));
+ goto out;
+ }
+ }
+ }
+
/* Get entry to the channel user list */
silc_hash_table_list(channel->user_list, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
mode &= ~SILC_CHANNEL_UMODE_CHANFO;
silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
- client->id, SILC_ID_CLIENT,
- client->id);
+ client2->id, SILC_ID_CLIENT,
+ client2->id);
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ idp = silc_id_payload_encode(client2->id, SILC_ID_CLIENT);
SILC_PUT32_MSB(mode, cumode);
silc_server_send_notify_to_channel(server, sock, channel, FALSE,
SILC_NOTIFY_TYPE_CUMODE_CHANGE,
if (chl2) {
chl2->mode = mode;
silc_free(channel_id);
+ silc_hash_table_list_reset(&htl);
goto out;
}
}
- if (chl->client == client) {
+ if (chl->client == client2) {
if (chl->mode == mode) {
notify_sent = TRUE;
break;
}
+ SILC_LOG_DEBUG(("Changing the channel user mode"));
+
/* Change the mode */
chl->mode = mode;
if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
chl2 = chl;
}
}
+ silc_hash_table_list_reset(&htl);
/* Send the same notify to the channel */
if (!notify_sent)
tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!channel_id)
goto out;
}
silc_free(channel_id);
- /* Get the added invite */
+ /* Get client ID */
tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (!tmp)
+ goto out;
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ if (!client_id)
+ goto out;
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, &cache);
+ if (!client) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
+
+ /* Get user's channel entry and check that inviting is allowed. */
+ if (!silc_server_client_on_channel(client, channel, &chl))
+ goto out;
+ if (chl->mode == SILC_CHANNEL_UMODE_NONE &&
+ channel->mode & SILC_CHANNEL_MODE_INVITE) {
+ SILC_LOG_DEBUG(("Inviting is not allowed"));
+ goto out;
+ }
+
+ /* Get the added invite */
+ tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
if (tmp) {
if (!channel->invite_list)
channel->invite_list = silc_calloc(tmp_len + 2,
}
/* Get the deleted invite */
- tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+ tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
if (tmp && channel->invite_list) {
char *start, *end, *n;
tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!channel_id)
goto out;
/* Get the channel entry */
- channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel = silc_idlist_find_channel_by_id(server->local_list,
channel_id, NULL);
if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel = silc_idlist_find_channel_by_id(server->global_list,
channel_id, NULL);
if (!channel) {
silc_free(channel_id);
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
if (!tmp)
goto out;
- channel_id2 = silc_id_payload_parse_id(tmp, tmp_len);
+ channel_id2 = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!channel_id2)
goto out;
silc_id_render(channel_id2, SILC_ID_CHANNEL)));
/* Replace the Channel ID */
- if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
+ if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
channel_id2))
- if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+ if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
channel_id2)) {
silc_free(channel_id2);
channel_id2 = NULL;
if (channel_id2) {
SilcBuffer users = NULL, users_modes = NULL;
-
+
+ /* Re-announce this channel which ID was changed. */
+ silc_server_send_new_channel(server, sock, FALSE, channel->channel_name,
+ channel->id,
+ silc_id_get_len(channel->id,
+ SILC_ID_CHANNEL),
+ channel->mode);
+
/* Re-announce our clients on the channel as the ID has changed now */
silc_server_announce_get_channel_users(server, channel, &users,
&users_modes);
users_modes->len, FALSE);
silc_buffer_free(users_modes);
}
+
+ /* Re-announce channel's topic */
+ if (channel->topic) {
+ silc_server_send_notify_topic_set(server, sock,
+ server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, channel,
+ channel->id, SILC_ID_CHANNEL,
+ channel->topic);
+ }
}
silc_free(channel_id);
tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
- server_id = silc_id_payload_parse_id(tmp, tmp_len);
+ server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!server_id)
goto out;
/* Get server entry */
server_entry = silc_idlist_find_server_by_id(server->global_list,
server_id, TRUE, NULL);
+ local = TRUE;
if (!server_entry) {
server_entry = silc_idlist_find_server_by_id(server->local_list,
server_id, TRUE, NULL);
+ local = TRUE;
if (!server_entry) {
+ /* If we are normal server then we might not have the server. Check
+ whether router was kind enough to send the list of all clients
+ that actually was to be removed. Remove them if the list is
+ available. */
+ if (server->server_type != SILC_ROUTER &&
+ silc_argument_get_arg_num(args) > 1) {
+ int i;
+
+ for (i = 1; i < silc_argument_get_arg_num(args); i++) {
+ /* Get Client ID */
+ tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
+ if (!tmp)
+ continue;
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ if (!client_id)
+ continue;
+
+ /* Get client entry */
+ client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, &cache);
+ local = TRUE;
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, &cache);
+ local = FALSE;
+ if (!client) {
+ silc_free(client_id);
+ continue;
+ }
+ }
+ silc_free(client_id);
+
+ /* Update statistics */
+ server->stat.clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+ /* Remove the client from all channels. */
+ silc_server_remove_from_channels(server, NULL, client,
+ TRUE, NULL, FALSE);
+
+ /* Remove the client */
+ silc_idlist_del_client(local ? server->local_list :
+ server->global_list, client);
+ }
+ }
+
silc_free(server_id);
goto out;
}
silc_server_remove_clients_by_server(server, server_entry, TRUE);
/* Remove the server entry */
- if (!silc_idlist_del_server(server->global_list, server_entry))
- silc_idlist_del_server(server->local_list, server_entry);
+ silc_idlist_del_server(local ? server->local_list :
+ server->global_list, server_entry);
/* XXX update statistics */
tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!client_id)
goto out;
goto out;
}
}
+ silc_free(client_id);
+
+ /* If target is founder they cannot be kicked */
+ if (!silc_server_client_on_channel(client, channel, &chl))
+ goto out;
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO)
+ goto out;
+
+ /* From protocol version 1.1 we get the kicker's ID as well. */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (tmp) {
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
+ if (!client_id)
+ goto out;
+
+ /* If the the client is not in local list we check global list */
+ client2 = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, NULL);
+ if (!client2) {
+ client2 = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!client2) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
+
+ /* Kicker must be operator on channel */
+ if (!silc_server_client_on_channel(client2, channel, &chl))
+ goto out;
+ if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+ SILC_LOG_DEBUG(("Kicking is not allowed"));
+ goto out;
+ }
+ }
/* Send to channel */
silc_server_packet_send_to_channel(server, sock, channel, packet->type,
/*
* Distribute the notify to local clients on channels
*/
- unsigned char *id;
- uint32 id_len;
+ unsigned char *id, *comment;
+ SilcUInt32 id_len, comment_len;
SILC_LOG_DEBUG(("KILLED notify"));
id = silc_argument_get_arg_type(args, 1, &id_len);
if (!id)
goto out;
- client_id = silc_id_payload_parse_id(id, id_len);
+ client_id = silc_id_payload_parse_id(id, id_len, NULL);
if (!client_id)
goto out;
}
/* Get comment */
- tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (tmp_len > 128)
- tmp = NULL;
+ comment = silc_argument_get_arg_type(args, 2, &comment_len);
+ if (comment_len > 128)
+ comment_len = 127;
+
+ /* From protocol version 1.1 we get the killer's ID as well. */
+ tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+ if (tmp) {
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
+ if (!client_id)
+ goto out;
+
+ if (id_type == SILC_ID_CLIENT) {
+ /* If the the client is not in local list we check global list */
+ client2 = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, NULL);
+ if (!client2) {
+ client2 = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!client2) {
+ silc_free(client_id);
+ goto out;
+ }
+ }
+ silc_free(client_id);
+
+ /* Killer must be router operator */
+ if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ SILC_LOG_DEBUG(("Killing is not allowed"));
+ goto out;
+ }
+ }
+ }
/* Send the notify to local clients on the channels except to the
client who is killed. */
silc_server_send_notify_on_channels(server, client, client,
- SILC_NOTIFY_TYPE_KILLED,
- tmp ? 2 : 1,
- id, id_len,
+ SILC_NOTIFY_TYPE_KILLED, 3,
+ id, id_len, comment, comment_len,
tmp, tmp_len);
/* Remove the client from all channels */
*/
SILC_LOG_DEBUG(("UMODE_CHANGE notify"));
-
+
/* 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);
+ client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!client_id)
goto out;
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
if (!tmp)
goto out;
+ SILC_GET32_MSB(mode, tmp);
- /* Save the mode */
- SILC_GET32_MSB(client->mode, tmp);
+ /* Check that mode changing is allowed. */
+ if (!silc_server_check_umode_rights(server, client, mode)) {
+ SILC_LOG_DEBUG(("UMODE change is not allowed"));
+ goto out;
+ }
+
+ /* Remove internal resumed flag if client is marked detached now */
+ if (mode & SILC_UMODE_DETACHED)
+ client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
+
+ /* Change the mode */
+ client->mode = mode;
break;
tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
if (!tmp)
goto out;
- channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
if (!channel_id)
goto out;
if (tmp && channel->ban_list) {
char *start, *end, *n;
- if (!strcmp(channel->ban_list, tmp)) {
+ if (!strncmp(channel->ban_list, tmp, strlen(channel->ban_list) - 1)) {
silc_free(channel->ban_list);
channel->ban_list = NULL;
} else {
}
}
}
-
break;
/* Ignore rest of the notify types for now */
{
SilcPacketContext *new;
SilcBuffer buffer;
- uint16 len;
+ SilcUInt16 len;
SILC_LOG_DEBUG(("Processing Notify List"));
{
SilcSocketConnection dst_sock;
SilcIDListData idata;
+ SilcClientEntry client;
SILC_LOG_DEBUG(("Start"));
if (packet->src_id_type != SILC_ID_CLIENT ||
- packet->dst_id_type != SILC_ID_CLIENT)
- return;
-
- if (!packet->dst_id)
+ packet->dst_id_type != SILC_ID_CLIENT || !packet->dst_id)
return;
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, NULL, &idata);
- if (!dst_sock)
+ packet->dst_id_len, NULL,
+ &idata, &client);
+ if (!dst_sock) {
+ SilcBuffer idp;
+
+ if (client && client->mode & SILC_UMODE_DETACHED) {
+ SILC_LOG_DEBUG(("Client is detached, discarding packet"));
+ return;
+ }
+
+ /* Send IDENTIFY command reply with error status to indicate that
+ such destination ID does not exist or is invalid */
+ idp = silc_id_payload_encode_data(packet->dst_id,
+ packet->dst_id_len,
+ packet->dst_id_type);
+ if (!idp)
+ return;
+
+ if (packet->src_id_type == SILC_ID_CLIENT) {
+ SilcClientID *client_id = silc_id_str2id(packet->src_id,
+ packet->src_id_len,
+ packet->src_id_type);
+ silc_server_send_dest_command_reply(server, sock,
+ client_id, SILC_ID_CLIENT,
+ SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+ 0, 0, 1, 2, idp->data, idp->len);
+ silc_free(client_id);
+ } else {
+ silc_server_send_command_reply(server, sock, SILC_COMMAND_IDENTIFY,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 0,
+ 0, 1, 2, idp->data, idp->len);
+ }
+
+ silc_buffer_free(idp);
return;
+ }
+
+ /* Check whether destination client wishes to receive private messages */
+ if (client && !(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) &&
+ client->mode & SILC_UMODE_BLOCK_PRIVMSG) {
+ SILC_LOG_DEBUG(("Client blocks private messages, discarding packet"));
+ return;
+ }
/* Send the private message */
silc_server_send_private_message(server, dst_sock, idata->send_key,
- idata->hmac_send, packet);
+ idata->hmac_send, idata->psn_send++,
+ packet);
}
/* Received private message key packet.. This packet is never for us. It is to
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, NULL, &idata);
+ packet->dst_id_len, NULL,
+ &idata, NULL);
if (!dst_sock)
return;
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac_send, packet, FALSE);
+ idata->hmac_send, idata->psn_send++, packet, FALSE);
}
/* Processes incoming command reply packet. The command reply packet may
if (packet->dst_id_type == SILC_ID_SERVER) {
/* For now this must be for us */
- if (memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
+ if (memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
return;
}
if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
/* Relay the packet to the client */
+ const SilcBufferStruct p;
dst_sock = (SilcSocketConnection)client->connection;
+ idata = (SilcIDListData)client;
+
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;
+ if (!silc_packet_send_prepare(dst_sock, 0, 0, buffer->len,
+ idata->hmac_send, (const SilcBuffer)&p)) {
+ SILC_LOG_ERROR(("Cannot send packet"));
+ return;
+ }
+ silc_buffer_put((SilcBuffer)&p, buffer->data, buffer->len);
/* Encrypt packet */
- silc_packet_encrypt(idata->send_key, idata->hmac_send, dst_sock->outbuf,
- buffer->len);
+ silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
+ (SilcBuffer)&p, buffer->len);
/* Send the packet */
silc_server_packet_send_real(server, dst_sock, TRUE);
{
SilcChannelEntry channel = NULL;
SilcChannelID *id = NULL;
- void *sender = NULL;
- void *sender_entry = NULL;
+ void *sender_id = NULL;
+ SilcClientEntry sender_entry = NULL;
+ SilcChannelClientEntry chl;
bool local = TRUE;
SILC_LOG_DEBUG(("Processing channel message"));
/* See that this client is on the channel. 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)
+ sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!sender_id)
goto out;
if (packet->src_id_type == SILC_ID_CLIENT) {
sender_entry = silc_idlist_find_client_by_id(server->local_list,
- sender, TRUE, NULL);
+ sender_id, TRUE, NULL);
if (!sender_entry) {
local = FALSE;
sender_entry = silc_idlist_find_client_by_id(server->global_list,
- sender, TRUE, NULL);
+ sender_id, TRUE, NULL);
}
if (!sender_entry || !silc_server_client_on_channel(sender_entry,
- channel)) {
+ channel, &chl)) {
SILC_LOG_DEBUG(("Client not on channel"));
goto out;
}
- /* If the packet is coming from router, but the client entry is
- local entry to us then some router is rerouting this to us and it is
- not allowed. */
+ /* If channel is moderated check that client is allowed to send
+ messages. */
+ if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chl->mode) {
+ SILC_LOG_DEBUG(("Channel is silenced from normal users"));
+ goto out;
+ }
+ if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
+ chl->mode & SILC_CHANNEL_UMODE_CHANOP &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+ SILC_LOG_DEBUG(("Channel is silenced from operators"));
+ goto out;
+ }
+
+ /* If the packet is coming from router, but the client entry is local
+ entry to us then some router is rerouting this to us and it is not
+ allowed. When the client is local to us it means that we've routed
+ this packet to network, and now someone is routing it back to us. */
if (server->server_type == SILC_ROUTER &&
sock->type == SILC_SOCKET_TYPE_ROUTER && local) {
SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it"));
/* 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,
+ silc_server_packet_relay_to_channel(server, sock, channel, sender_id,
packet->src_id_type, sender_entry,
packet->buffer->data,
packet->buffer->len, FALSE);
out:
- if (sender)
- silc_free(sender);
- if (id)
- silc_free(id);
+ silc_free(sender_id);
+ silc_free(id);
}
/* Received channel key packet. We distribute the key to all of our locally
SilcBuffer buffer = packet->buffer;
SilcClientEntry client;
SilcClientID *client_id;
- SilcBuffer reply;
SilcIDListData idata;
- char *username = NULL, *realname = NULL, *id_string;
- uint32 id_len;
+ char *username = NULL, *realname = NULL;
+ SilcUInt16 username_len;
+ SilcUInt32 id_len;
int ret;
char *hostname, *nickname;
int nickfail = 0;
client = (SilcClientEntry)sock->user_data;
idata = (SilcIDListData)client;
- /* Remove the old cache entry */
+ /* Remove the old cache entry. */
if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
- SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
+ SILC_LOG_INFO(("Unauthenticated client attempted to register to network"));
silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Unknown client");
+ "You have not been authenticated");
return NULL;
}
/* Parse incoming packet */
ret = silc_buffer_unformat(buffer,
- SILC_STR_UI16_STRING_ALLOC(&username),
+ SILC_STR_UI16_NSTRING_ALLOC(&username,
+ &username_len),
SILC_STR_UI16_STRING_ALLOC(&realname),
SILC_STR_END);
if (ret == -1) {
- if (username)
- silc_free(username);
- if (realname)
- silc_free(realname);
+ silc_free(username);
+ silc_free(realname);
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
+ "connection", sock->hostname, sock->ip));
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Incomplete client information");
return NULL;
if (!username) {
silc_free(username);
- if (realname)
- silc_free(realname);
+ silc_free(realname);
+ SILC_LOG_ERROR(("Client %s (%s) did not send its username, closing "
+ "connection", sock->hostname, sock->ip));
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Incomplete client information");
return NULL;
}
- if (strlen(username) > 128)
- username[127] = '\0';
+ if (username_len > 128)
+ username[128] = '\0';
- nickname = strdup(username);
+ /* Check for bad characters for nickname, and modify the nickname if
+ it includes those. */
+ if (silc_server_name_bad_chars(username, username_len)) {
+ nickname = silc_server_name_modify_bad(username, username_len);
+ } else {
+ nickname = strdup(username);
+ }
/* Make sanity checks for the hostname of the client. If the hostname
is provided in the `username' check that it is the same than the
int tlen = strcspn(username, "@");
char *phostname = NULL;
- hostname = silc_calloc((strlen(username) - tlen) + 1, sizeof(char));
- memcpy(hostname, username + tlen + 1, strlen(username) - tlen - 1);
+ hostname = silc_memdup(username + tlen + 1, strlen(username) - tlen - 1);
if (strcmp(sock->hostname, sock->ip) &&
strcmp(sock->hostname, hostname)) {
silc_free(username);
silc_free(hostname);
- if (realname)
- silc_free(realname);
+ silc_free(realname);
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
+ "connection", sock->hostname, sock->ip));
silc_server_disconnect_remote(server, sock,
"Server closed connection: "
"Incomplete client information");
phostname && strcmp(phostname, hostname)) {
silc_free(username);
silc_free(hostname);
- if (phostname)
- silc_free(phostname);
- if (realname)
- silc_free(realname);
+ silc_free(phostname);
+ silc_free(realname);
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete information, closing "
+ "connection", sock->hostname, sock->ip));
silc_server_disconnect_remote(server, sock,
"Server closed connection: "
"Incomplete client information");
return NULL;
}
- if (phostname)
- silc_free(phostname);
+ silc_free(phostname);
} else {
/* The hostname is not present, add it. */
char *newusername;
while (!silc_id_create_client_id(server, server->id, server->rng,
server->md5hash, nickname, &client_id)) {
nickfail++;
+ if (nickfail > 9) {
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: Bad nickname");
+ return NULL;
+ }
snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail);
}
/* Add the client again to the ID cache */
silc_idcache_add(server->local_list->clients, client->nickname,
- client_id, client, FALSE);
+ client_id, client, 0, NULL);
/* Notify our router about new client on the SILC network */
if (!server->standalone)
client->id, SILC_ID_CLIENT, id_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 + id_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(id_len),
- SILC_STR_UI_XNSTRING(id_string, id_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);
+ silc_server_send_new_id(server, sock, FALSE, client->id, SILC_ID_CLIENT,
+ silc_id_get_len(client->id, SILC_ID_CLIENT));
/* Send some nice info to the client */
- SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
- ("Welcome to the SILC Network %s",
- username));
- 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 motd */
- silc_server_send_motd(server, sock);
+ silc_server_send_connect_notifys(server, sock, client);
return client;
}
SilcServerID *server_id;
SilcIDListData idata;
unsigned char *server_name, *id_string;
- uint16 id_len, name_len;
+ SilcUInt16 id_len, name_len;
int ret;
bool local = TRUE;
/* Remove the old cache entry */
if (!silc_idcache_del_by_context(server->local_list->servers, new_server)) {
- silc_idcache_del_by_context(server->global_list->servers, new_server);
+ if (!silc_idcache_del_by_context(server->global_list->servers,
+ new_server)) {
+ SILC_LOG_INFO(("Unauthenticated %s attempted to register to "
+ "network", (sock->type == SILC_SOCKET_TYPE_SERVER ?
+ "server" : "router")));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "You have not been authenticated");
+ return NULL;
+ }
local = FALSE;
}
}
silc_free(id_string);
+ /* Check for valid server ID */
+ if (!silc_id_is_valid_server_id(server, server_id, sock)) {
+ SILC_LOG_INFO(("Invalid server ID sent by %s (%s)",
+ sock->ip, sock->hostname));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Your Server ID is not valid");
+ silc_free(server_name);
+ return NULL;
+ }
+
/* Check that we do not have this ID already */
server_entry = silc_idlist_find_server_by_id(server->local_list,
server_id, TRUE, NULL);
/* Add again the entry to the ID cache. */
silc_idcache_add(local ? server->local_list->servers :
server->global_list->servers, server_name, server_id,
- new_server, FALSE);
+ new_server, 0, NULL);
/* Distribute the information about new server in the SILC network
to our router. If we are normal server we won't send anything
packet->src_id_type != SILC_ID_SERVER)
return;
- idp = silc_id_payload_parse(buffer);
+ idp = silc_id_payload_parse(buffer->data, buffer->len);
if (!idp)
return;
router_sock = sock;
router = sock->user_data;
+ /* If the sender is backup router and ID is server (and we are not
+ backup router) then switch the entry to global list. */
if (server_entry->server_type == SILC_BACKUP_ROUTER &&
- id_type == SILC_ID_SERVER) {
+ id_type == SILC_ID_SERVER &&
+ server->id_entry->server_type != SILC_BACKUP_ROUTER) {
id_list = server->global_list;
router_sock = server->router ? server->router->connection : sock;
}
router = silc_idlist_find_server_by_id(server->local_list,
sender_id, TRUE, NULL);
silc_free(sender_id);
- if (!router)
- goto out;
router_sock = sock;
id_list = server->global_list;
}
+ if (!router)
+ goto out;
+
switch(id_type) {
case SILC_ID_CLIENT:
{
global list. Cell wide information however is kept in the local
list. */
entry = silc_idlist_add_client(id_list, NULL, NULL, NULL,
- id, router, NULL);
+ id, router, NULL, 0);
if (!entry) {
SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
{
SilcPacketContext *new_id;
SilcBuffer idp;
- uint16 id_len;
+ SilcUInt16 id_len;
SILC_LOG_DEBUG(("Processing New ID List"));
SilcChannelPayload payload;
SilcChannelID *channel_id;
char *channel_name;
- uint32 name_len;
+ SilcUInt32 name_len;
unsigned char *id;
- uint32 id_len;
- uint32 mode;
+ SilcUInt32 id_len;
+ SilcUInt32 mode;
SilcServerEntry server_entry;
SilcChannelEntry channel;
return;
/* Parse the channel payload */
- payload = silc_channel_payload_parse(packet->buffer);
+ payload = silc_channel_payload_parse(packet->buffer->data,
+ packet->buffer->len);
if (!payload)
return;
silc_id_render(channel_id, SILC_ID_CHANNEL),
sock->hostname));
- silc_idlist_add_channel(server->global_list, strdup(channel_name),
- 0, channel_id, sock->user_data, NULL, NULL);
+ channel =
+ silc_idlist_add_channel(server->global_list, strdup(channel_name),
+ 0, channel_id, sock->user_data, NULL, NULL, 0);
+ if (!channel)
+ return;
+
server->stat.channels++;
+ if (server->server_type == SILC_ROUTER)
+ channel->users_resolved = TRUE;
}
} else {
/* The channel is coming from our server, thus it is in our cell
channel->mode = silc_channel_get_mode(payload);
/* Send the new channel key to the server */
+ id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
chk = silc_channel_key_payload_encode(id_len, id,
strlen(channel->channel_key->
cipher->name),
We also create a new key for the channel. */
SilcBuffer users = NULL, users_modes = NULL;
- if (!channel->id)
- channel->id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
-
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_server_send_notify_cmode(server, sock, FALSE, channel,
channel->mode, server->id,
SILC_ID_SERVER,
- channel->cipher, channel->hmac_name);
+ channel->cipher, channel->hmac_name,
+ channel->passphrase);
}
/* Create new key for the channel and send it to the server and
/* Send to the channel */
silc_server_send_channel_key(server, sock, channel, FALSE);
id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
- id_len = SILC_ID_CHANNEL_LEN;
-
+ id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+
/* Send to the server */
chk = silc_channel_key_payload_encode(id_len, id,
strlen(channel->channel_key->
{
SilcPacketContext *new;
SilcBuffer buffer;
- uint16 len1, len2;
+ SilcUInt16 len1, len2;
SILC_LOG_DEBUG(("Processing New Channel List"));
/* Get the route to the client */
dst_sock = silc_server_get_client_route(server, packet->dst_id,
- packet->dst_id_len, NULL, &idata);
+ packet->dst_id_len, NULL,
+ &idata, NULL);
if (!dst_sock)
return;
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac_send, packet, FALSE);
+ idata->hmac_send, idata->psn_send++,
+ packet, FALSE);
}
/* Received connection auth request packet that is used during connection
SilcSocketConnection sock,
SilcPacketContext *packet)
{
- SilcServerConfigSectionClientConnection *client = NULL;
- uint16 conn_type;
- int ret, port;
- SilcAuthMethod auth_meth;
+ SilcServerConfigClient *client = NULL;
+ SilcUInt16 conn_type;
+ int ret;
+ SilcAuthMethod auth_meth = SILC_AUTH_NONE;
SILC_LOG_DEBUG(("Start"));
/* Get the authentication method for the client */
auth_meth = SILC_AUTH_NONE;
- port = server->sockets[server->sock]->port; /* Listenning port */
- client = silc_server_config_find_client_conn(server->config,
- sock->ip,
- port);
+ client = silc_server_config_find_client(server, sock->ip);
if (!client)
- client = silc_server_config_find_client_conn(server->config,
- sock->hostname,
- port);
- if (client)
- auth_meth = client->auth_meth;
-
+ client = silc_server_config_find_client(server, sock->hostname);
+ if (client) {
+ if (client->passphrase) {
+ if (client->publickeys && !server->config->prefer_passphrase_auth)
+ auth_meth = SILC_AUTH_PUBLIC_KEY;
+ else
+ auth_meth = SILC_AUTH_PASSWORD;
+ } else if (client->publickeys)
+ auth_meth = SILC_AUTH_PUBLIC_KEY;
+ }
+
/* Send it back to the client */
- silc_server_send_connection_auth_request(server, sock,
- conn_type,
- auth_meth);
+ silc_server_send_connection_auth_request(server, sock, conn_type, auth_meth);
}
/* Received REKEY packet. The sender of the packet wants to regenerate
/* Run the protocol */
silc_protocol_execute(protocol, server->schedule, 0, 0);
}
+
+/* Received file transger 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_ftp(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcSocketConnection dst_sock;
+ SilcIDListData idata;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (packet->src_id_type != SILC_ID_CLIENT ||
+ packet->dst_id_type != SILC_ID_CLIENT)
+ return;
+
+ if (!packet->dst_id)
+ return;
+
+ /* Get the route to the client */
+ dst_sock = silc_server_get_client_route(server, packet->dst_id,
+ packet->dst_id_len, NULL,
+ &idata, NULL);
+ if (!dst_sock)
+ return;
+
+ /* Relay the packet */
+ silc_server_relay_packet(server, dst_sock, idata->send_key,
+ idata->hmac_send, idata->psn_send++,
+ packet, FALSE);
+}
+
+typedef struct {
+ SilcServer server;
+ SilcSocketConnection sock;
+ SilcPacketContext *packet;
+ void *data;
+} *SilcServerResumeResolve;
+
+SILC_SERVER_CMD_FUNC(resume_resolve)
+{
+ SilcServerResumeResolve r = (SilcServerResumeResolve)context;
+ SilcServer server = r->server;
+ SilcSocketConnection sock = r->sock;
+ SilcServerCommandReplyContext reply = context2;
+ SilcClientEntry client;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (!reply || !silc_command_get_status(reply->payload, NULL, NULL)) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ goto out;
+ }
+
+ if (reply && silc_command_get(reply->payload) == SILC_COMMAND_WHOIS) {
+ /* Get entry to the client, and resolve it if we don't have it. */
+ client = silc_idlist_find_client_by_id(server->local_list,
+ r->data, TRUE, NULL);
+ if (!client) {
+ client = silc_idlist_find_client_by_id(server->global_list,
+ r->data, TRUE, NULL);
+ if (!client) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ goto out;
+ }
+ }
+
+ if (!(client->mode & SILC_UMODE_DETACHED)) {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ goto out;
+ }
+ }
+
+ /* Reprocess the packet */
+ silc_server_resume_client(server, sock, r->packet);
+
+ out:
+ silc_socket_free(r->sock);
+ silc_packet_context_free(r->packet);
+ silc_free(r->data);
+ silc_free(r);
+}
+
+/* Received client resuming packet. This is used to resume detached
+ client session. It can be sent by the client who wishes to resume
+ but this is also sent by servers and routers to notify other routers
+ that the client is not detached anymore. */
+
+void silc_server_resume_client(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcBuffer buffer = packet->buffer, buf;
+ SilcIDListData idata;
+ SilcClientEntry detached_client;
+ SilcClientID *client_id = NULL;
+ unsigned char *id_string, *auth = NULL;
+ SilcUInt16 id_len, auth_len = 0;
+ int ret, nickfail = 0;
+ bool resolved, local, nick_change = FALSE;
+ SilcChannelEntry channel;
+ SilcHashTableList htl;
+ SilcChannelClientEntry chl;
+ SilcServerResumeResolve r;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ ret = silc_buffer_unformat(buffer,
+ SILC_STR_UI16_NSTRING(&id_string, &id_len),
+ SILC_STR_END);
+ if (ret != -1)
+ client_id = silc_id_str2id(id_string, id_len, SILC_ID_CLIENT);
+
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ /* Client send this and is attempting to resume to old client session */
+ SilcClientEntry client;
+ SilcBuffer keyp;
+
+ if (ret != -1) {
+ silc_buffer_pull(buffer, 2 + id_len);
+ auth = buffer->data;
+ auth_len = buffer->len;
+ silc_buffer_push(buffer, 2 + id_len);
+ }
+
+ if (!client_id || auth_len < 128) {
+ SILC_LOG_ERROR(("Client %s (%s) sent incomplete resume information, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete resume information");
+ return;
+ }
+
+ /* Take client entry of this connection */
+ client = (SilcClientEntry)sock->user_data;
+ idata = (SilcIDListData)client;
+
+ /* Get entry to the client, and resolve it if we don't have it. */
+ detached_client = silc_server_get_client_resolve(server, client_id, FALSE,
+ &resolved);
+ if (!detached_client) {
+ if (resolved) {
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ SILC_LOG_DEBUG(("Resolving client"));
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+ } else {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume unknown client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ }
+ return;
+ }
+
+ /* Check that the client is detached, and that we have other info too */
+ if (!(detached_client->mode & SILC_UMODE_DETACHED) ||
+ !silc_hash_table_count(detached_client->channels) ||
+ !detached_client->nickname) {
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ SILC_LOG_DEBUG(("Resolving client info"));
+ silc_server_get_client_resolve(server, client_id, TRUE, NULL);
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+ } else {
+ SILC_LOG_ERROR(("Client %s (%s) tried to resume un-detached client, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ }
+ return;
+ }
+
+ /* Check that we have the public key of the client, if not then we must
+ resolve it first. */
+ if (!detached_client->data.public_key) {
+ if (server->standalone) {
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Incomplete resume information");
+ } else {
+ /* We must retrieve the detached client's public key by sending
+ GETKEY command. Reprocess this packet after receiving the key */
+ SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ SilcSocketConnection dest_sock =
+ silc_server_get_client_route(server, NULL, 0, client_id, NULL, NULL);
+
+ SILC_LOG_DEBUG(("Resolving client public key"));
+
+ silc_server_send_command(server, dest_sock ? dest_sock :
+ server->router->connection,
+ SILC_COMMAND_GETKEY, ++server->cmd_ident,
+ 1, 1, idp->data, idp->len);
+
+ r = silc_calloc(1, sizeof(*r));
+ if (!r)
+ return;
+
+ r->server = server;
+ r->sock = silc_socket_dup(sock);
+ r->packet = silc_packet_context_dup(packet);
+ silc_server_command_pending(server, SILC_COMMAND_GETKEY,
+ server->cmd_ident,
+ silc_server_command_resume_resolve, r);
+
+ silc_buffer_free(idp);
+ }
+ return;
+ }
+
+ /* Verify the authentication payload. This has to be successful in
+ order to allow the resuming */
+ if (!silc_auth_verify_data(auth, auth_len, SILC_AUTH_PUBLIC_KEY,
+ detached_client->data.public_key, 0,
+ idata->hash, detached_client->id,
+ SILC_ID_CLIENT)) {
+ SILC_LOG_ERROR(("Client %s (%s) resume authentication failed, "
+ "closing connection", sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete resume information");
+ return;
+ }
+
+ /* Now resume the client to the network */
+
+ sock->user_data = detached_client;
+ detached_client->connection = sock;
+
+ /* Take new keys and stuff into use in the old entry */
+ silc_idlist_del_data(detached_client);
+ silc_idlist_add_data(detached_client, idata);
+ detached_client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+ detached_client->mode &= ~SILC_UMODE_DETACHED;
+
+ /* Send the RESUME_CLIENT packet to our primary router so that others
+ know this client isn't detached anymore. */
+ buf = silc_buffer_alloc_size(2 + id_len);
+ silc_buffer_format(buf,
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_UI_XNSTRING(id_string, id_len),
+ SILC_STR_END);
+
+ /* Send to primary router */
+ if (!server->standalone)
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buf->data, buf->len, TRUE);
+
+ /* As router we must deliver this packet directly to the original
+ server whom this client was earlier. */
+ if (server->server_type == SILC_ROUTER && detached_client->router)
+ silc_server_packet_send(server, detached_client->router->connection,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buf->data, buf->len, TRUE);
+ silc_buffer_free(buf);
+
+ detached_client->router = NULL;
+
+ /* Delete this client entry since we're resuming to old one. */
+ server->stat.my_clients--;
+ server->stat.clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ silc_idlist_del_client(server->local_list, client);
+ client = detached_client;
+
+ /* If the ID is not based in our ID then change it */
+ if (!SILC_ID_COMPARE(client->id, server->id, server->id->ip.data_len)) {
+ while (!silc_id_create_client_id(server, server->id, server->rng,
+ server->md5hash, client->nickname,
+ &client_id)) {
+ nickfail++;
+ if (nickfail > 9) {
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Bad nickname");
+ return;
+ }
+ snprintf(&client->nickname[strlen(client->nickname) - 1], 1,
+ "%d", nickfail);
+ }
+ nick_change = TRUE;
+ }
+
+ if (nick_change) {
+ /* Notify about Client ID change, nickname doesn't actually change. */
+ if (!server->standalone)
+ silc_server_send_notify_nick_change(server, server->router->connection,
+ FALSE, client->id, client_id,
+ client->nickname);
+ }
+
+ /* Resolve users on those channels that client has joined but we
+ haven't resolved user list yet. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ channel = chl->channel;
+ SILC_LOG_DEBUG(("Resolving users for %s channel",
+ channel->channel_name));
+ if (channel->disabled || !channel->users_resolved) {
+ silc_server_send_command(server, server->router->connection,
+ SILC_COMMAND_USERS, ++server->cmd_ident,
+ 1, 2, channel->channel_name,
+ strlen(channel->channel_name));
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ }
+
+ /* Send the new client ID to the client. After this client may start
+ receiving other packets, and may start sending packets too. */
+ silc_server_send_new_id(server, sock, FALSE, client_id, SILC_ID_CLIENT,
+ silc_id_get_len(client_id, SILC_ID_CLIENT));
+
+ if (nick_change) {
+ /* Send NICK change notify to channels as well. */
+ SilcBuffer oidp, nidp;
+ oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ nidp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+ silc_server_send_notify_on_channels(server, NULL, client,
+ SILC_NOTIFY_TYPE_NICK_CHANGE, 3,
+ oidp->data, oidp->len,
+ nidp->data, nidp->len,
+ client->nickname,
+ strlen(client->nickname));
+ silc_buffer_free(oidp);
+ silc_buffer_free(nidp);
+ }
+
+ /* Add the client again to the ID cache to get it to correct list */
+ if (!silc_idcache_del_by_context(server->local_list->clients, client))
+ silc_idcache_del_by_context(server->global_list->clients, client);
+ silc_free(client->id);
+ client->id = client_id;
+ client_id = NULL;
+ silc_idcache_add(server->local_list->clients, client->nickname,
+ client->id, client, 0, NULL);
+
+ /* Send some nice info to the client */
+ silc_server_send_connect_notifys(server, sock, client);
+
+ /* Send all channel keys of channels the client has joined */
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ bool created = FALSE;
+ channel = chl->channel;
+
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
+ continue;
+
+ /* If we don't have channel key, then create one */
+ if (!channel->channel_key) {
+ if (!silc_server_create_channel_key(server, channel, 0))
+ continue;
+ created = TRUE;
+ }
+
+ id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ keyp =
+ silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+ SILC_ID_CHANNEL),
+ id_string,
+ strlen(channel->channel_key->
+ cipher->name),
+ channel->channel_key->cipher->name,
+ channel->key_len / 8, channel->key);
+ silc_free(id_string);
+
+ /* Send the key packet to client */
+ silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0,
+ keyp->data, keyp->len, FALSE);
+
+ if (created && server->server_type == SILC_SERVER &&
+ !server->standalone)
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_CHANNEL_KEY, 0,
+ keyp->data, keyp->len, FALSE);
+
+ silc_buffer_free(keyp);
+ }
+ silc_hash_table_list_reset(&htl);
+
+ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+ ("Your session is successfully resumed"));
+
+ } else if (sock->type != SILC_SOCKET_TYPE_CLIENT) {
+ /* Server or router sent this to us to notify that that a client has
+ been resumed. */
+ SilcServerEntry server_entry;
+ SilcServerID *server_id;
+
+ if (!client_id)
+ return;
+
+ /* Get entry to the client, and resolve it if we don't have it. */
+ detached_client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!detached_client) {
+ detached_client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, NULL);
+ if (!detached_client)
+ return;
+ }
+
+ /* Check that the client has not been resumed already because it is
+ protocol error to attempt to resume more than once. The client
+ will be killed if this protocol error occurs. */
+ if (detached_client->data.status & SILC_IDLIST_STATUS_RESUMED &&
+ !(detached_client->mode & SILC_UMODE_DETACHED)) {
+ /* The client is clearly attempting to resume more than once and
+ perhaps playing around by resuming from several different places
+ at the same time. */
+ silc_server_kill_client(server, detached_client, NULL,
+ server->id, SILC_ID_SERVER);
+ return;
+ }
+
+ /* Check whether client is detached at all */
+ if (!(detached_client->mode & SILC_UMODE_DETACHED))
+ return;
+
+ /* Client is detached, and now it is resumed. Remove the detached
+ mode and mark that it is resumed. */
+ detached_client->mode &= ~SILC_UMODE_DETACHED;
+ detached_client->data.status |= SILC_IDLIST_STATUS_RESUMED;
+
+ /* Get the new owner of the resumed client */
+ server_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!server_id)
+ return;
+
+ /* Get server entry */
+ server_entry = silc_idlist_find_server_by_id(server->global_list,
+ server_id, TRUE, NULL);
+ local = TRUE;
+ if (!server_entry) {
+ server_entry = silc_idlist_find_server_by_id(server->local_list,
+ server_id, TRUE, NULL);
+ local = FALSE;
+ if (!server_entry) {
+ silc_free(server_id);
+ return;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Resuming detached client"));
+
+ /* Change the client to correct list. */
+ if (!silc_idcache_del_by_context(server->local_list->clients,
+ detached_client))
+ silc_idcache_del_by_context(server->global_list->clients,
+ detached_client);
+ silc_idcache_add(local && server->server_type == SILC_ROUTER ?
+ server->local_list->clients :
+ server->global_list->clients,
+ detached_client->nickname,
+ detached_client->id, detached_client, FALSE, NULL);
+
+ /* Change the owner of the client if needed */
+ if (detached_client->router != server_entry)
+ detached_client->router = server_entry;
+
+ /* Update channel information regarding global clients on channel. */
+ if (server->server_type == SILC_SERVER) {
+ silc_hash_table_list(detached_client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl))
+ chl->channel->global_users =
+ silc_server_channel_has_global(chl->channel);
+ silc_hash_table_list_reset(&htl);
+ }
+
+ /* 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 (!server->standalone && server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_SERVER &&
+ !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+ SILC_LOG_DEBUG(("Broadcasting received Resume Client packet"));
+ silc_server_packet_send(server, server->router->connection,
+ packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ buffer->data, buffer->len, FALSE);
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ packet->type, packet->flags,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
+ }
+
+ silc_free(server_id);
+ }
+
+ silc_free(client_id);
+}