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. */
SilcNotifyPayload payload;
SilcNotifyType type;
SilcArgumentPayload args;
- SilcChannelID *channel_id, *channel_id2;
+ 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->data,
+ packet->buffer->len);
+ if (!payload)
+ return;
+
/* 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
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 (packet->dst_id_type == SILC_ID_CHANNEL) {
+ /* Packet is destined to channel */
+ channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ packet->dst_id_type);
+ if (!channel_id)
+ goto out;
- payload = silc_notify_payload_parse(packet->buffer);
- if (!payload)
- return;
+ silc_server_packet_send_dest(server, server->router->connection,
+ packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ channel_id, SILC_ID_CHANNEL,
+ packet->buffer->data, packet->buffer->len,
+ FALSE);
+ silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data,
+ packet->type, packet->flags,
+ channel_id, SILC_ID_CHANNEL,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
+ } else {
+ /* Packet is destined to client or server */
+ silc_server_packet_send(server, server->router->connection,
+ packet->type,
+ packet->flags | SILC_PACKET_FLAG_BROADCAST,
+ packet->buffer->data, packet->buffer->len,
+ FALSE);
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ packet->type, packet->flags,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
+ }
+ }
type = silc_notify_get_type(payload);
args = silc_notify_get_args(payload);
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;
- /* Send to channel */
- silc_server_packet_send_to_channel(server, sock, channel, packet->type,
- FALSE, packet->buffer->data,
- packet->buffer->len, FALSE);
-
/* 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->global_list,
- client_id, NULL);
+ client_id, server->server_type,
+ NULL);
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
+ client_id, server->server_type,
+ NULL);
if (!client) {
/* If router did not find the client the it is bogus */
- if (server->server_type == SILC_ROUTER)
+ if (server->server_type != SILC_SERVER)
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);
goto out;
}
- client->data.registered = TRUE;
+ client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
}
}
/* Do not process the notify if the client is not registered */
- if (client->data.registered == FALSE)
+ if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
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;
+ }
+
+ /* Send to channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
- if (server->server_type == SILC_SERVER &&
+ if (server->server_type != SILC_ROUTER &&
sock->type == SILC_SOCKET_TYPE_ROUTER)
/* 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));
chl->client = client;
chl->channel = channel;
+
+ /* If this is the first one on the channel then it is the founder of
+ the channel. */
+ if (!silc_hash_table_count(channel->user_list))
+ chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
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_LOG_DEBUG(("LEAVE notify"));
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- packet->dst_id_type);
- if (!channel_id)
- goto out;
+ if (!channel_id) {
+ 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->global_list,
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;
}
- /* 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);
+ client_id, TRUE, NULL);
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client) {
silc_free(client_id);
silc_free(channel_id);
}
silc_free(client_id);
+ /* Check if on channel */
+ if (!silc_server_client_on_channel(client, channel, NULL))
+ break;
+
+ /* Send the leave notify to channel */
+ silc_server_packet_send_to_channel(server, sock, channel, packet->type,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
+
/* Remove the user from channel */
silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
break;
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;
/* Get client entry */
client = silc_idlist_find_client_by_id(server->global_list,
- client_id, &cache);
+ client_id, TRUE, &cache);
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
- client_id, &cache);
+ client_id, TRUE, &cache);
if (!client) {
silc_free(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.registered = 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"));
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- packet->dst_id_type);
- if (!channel_id)
+ /* 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);
+ if (!channel_id)
+ goto out;
+ }
/* Get channel entry */
channel = silc_idlist_find_channel_by_id(server->global_list,
}
}
- /* 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"));
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- packet->dst_id_type);
- if (!channel_id)
+ /* 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);
+ if (!channel_id)
+ goto out;
+ }
/* Get channel entry */
channel = silc_idlist_find_channel_by_id(server->global_list,
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);
+ 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;
+ }
}
- SILC_GET32_MSB(mode, tmp);
+ /* 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);
/* If the channel had private keys set and the mode was removed then
we must re-generate and re-distribute a new channel key */
/* 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"));
- channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- packet->dst_id_type);
- if (!channel_id)
+ /* 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);
+ if (!channel_id)
+ goto out;
+ }
+
/* Get channel entry */
channel = silc_idlist_find_channel_by_id(server->global_list,
channel_id, NULL);
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, NULL);
- if (!client) {
- client = silc_idlist_find_client_by_id(server->local_list,
- client_id, 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)) {
SilcBuffer idp;
unsigned char cumode[4];
+ if (chl->client == client && chl->mode == mode) {
+ notify_sent = TRUE;
+ break;
+ }
+
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, NULL);
+ server_id, TRUE, NULL);
+ local = TRUE;
if (!server_entry) {
server_entry = silc_idlist_find_server_by_id(server->local_list,
- server_id, NULL);
+ 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 */
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;
+ if (!channel_id) {
+ 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->global_list,
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;
- /* Send to channel */
- silc_server_packet_send_to_channel(server, sock, channel, packet->type,
- FALSE, packet->buffer->data,
- packet->buffer->len, FALSE);
-
/* If the the client is not in local list we check global list */
client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client) {
silc_free(client_id);
goto out;
}
}
+ silc_free(client_id);
- /* Remove the client from channel */
- silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
-
- break;
-
- case SILC_NOTIFY_TYPE_KILLED:
- {
- /*
- * Distribute the notify to local clients on channels
- */
- unsigned char *id;
- uint32 id_len;
+ /* 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;
- SILC_LOG_DEBUG(("KILLED notify"));
-
- /* Get client ID */
- id = silc_argument_get_arg_type(args, 1, &id_len);
- if (!id)
- goto out;
- client_id = silc_id_payload_parse_id(id, id_len);
+ /* 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,
+ FALSE, packet->buffer->data,
+ packet->buffer->len, FALSE);
+
+ /* Remove the client from channel */
+ silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+
+ break;
+
+ case SILC_NOTIFY_TYPE_KILLED:
+ {
+ /*
+ * Distribute the notify to local clients on channels
+ */
+ unsigned char *id, *comment;
+ SilcUInt32 id_len, comment_len;
+
+ SILC_LOG_DEBUG(("KILLED notify"));
+
+ /* Get client ID */
+ id = silc_argument_get_arg_type(args, 1, &id_len);
+ if (!id)
+ goto out;
+ client_id = silc_id_payload_parse_id(id, id_len, NULL);
if (!client_id)
goto out;
/* If the the client is not in local list we check global list */
client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client) {
silc_free(client_id);
goto out;
/* If the client is one of ours, then close the connection to the
client now. This removes the client from all channels as well. */
- if (packet->dst_id_type == SILC_ID_CLIENT && client->data.registered &&
- client->connection) {
+ if (packet->dst_id_type == SILC_ID_CLIENT && client->connection) {
sock = client->connection;
silc_server_free_client_data(server, NULL, client, FALSE, NULL);
silc_server_close_connection(server, sock);
}
/* 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;
/* Get client entry */
client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
- client_id, NULL);
+ client_id, TRUE, NULL);
if (!client) {
silc_free(client_id);
goto out;
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
if (!tmp)
goto out;
+ SILC_GET32_MSB(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;
- /* Save the mode */
- SILC_GET32_MSB(client->mode, tmp);
+ /* 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
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);
+ client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
if (!client) {
SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
silc_free(id);
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, NULL);
- if (!sender_entry)
+ sender_id, TRUE, NULL);
+ if (!sender_entry) {
+ local = FALSE;
sender_entry = silc_idlist_find_client_by_id(server->global_list,
- sender, 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 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"));
+ 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,
+ 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
/* 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);
+
+ if (server->server_type != SILC_BACKUP_ROUTER) {
+ /* Distribute to local cell backup routers. */
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ SILC_PACKET_CHANNEL_KEY, 0,
+ buffer->data, buffer->len, FALSE, TRUE);
+ }
}
/* Received New Client packet and processes it. Creates Client ID for the
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;
SILC_LOG_DEBUG(("Creating new client"));
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;
}
/* Create Client ID */
- silc_id_create_client_id(server->id, server->rng, server->md5hash,
- username, &client_id);
+ 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);
+ }
/* Update client entry */
- idata->registered = TRUE;
+ idata->status |= SILC_IDLIST_STATUS_REGISTERED;
client->nickname = nickname;
client->username = username;
client->userinfo = realname ? realname : strdup(" ");
/* 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;
}
SilcPacketContext *packet)
{
SilcBuffer buffer = packet->buffer;
- SilcServerEntry new_server;
+ SilcServerEntry new_server, server_entry;
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;
SILC_LOG_DEBUG(("Creating new server"));
idata = (SilcIDListData)new_server;
/* Remove the old cache entry */
- silc_idcache_del_by_context(server->local_list->servers, new_server);
+ if (!silc_idcache_del_by_context(server->local_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;
+ }
/* Parse the incoming packet */
ret = silc_buffer_unformat(buffer,
}
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);
+ if (server_entry) {
+ silc_idcache_del_by_context(server->local_list->servers, server_entry);
+ } else {
+ server_entry = silc_idlist_find_server_by_id(server->global_list,
+ server_id, TRUE, NULL);
+ if (server_entry)
+ silc_idcache_del_by_context(server->global_list->servers, server_entry);
+ }
+
/* Update server entry */
- idata->registered = TRUE;
+ idata->status |= SILC_IDLIST_STATUS_REGISTERED;
new_server->server_name = server_name;
new_server->id = server_id;
+
+ SILC_LOG_DEBUG(("New server id(%s)",
+ silc_id_render(server_id, SILC_ID_SERVER)));
/* Add again the entry to the ID cache. */
- silc_idcache_add(server->local_list->servers, server_name, server_id,
- server, FALSE);
+ silc_idcache_add(local ? server->local_list->servers :
+ server->global_list->servers, server_name, server_id,
+ 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
if (server->server_type == SILC_ROUTER)
server->stat.cell_servers++;
+ /* Check whether this router connection has been replaced by an
+ backup router. If it has been then we'll disable the server and will
+ ignore everything it will send until the backup router resuming
+ protocol has been completed. */
+ if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+ silc_server_backup_replaced_get(server, server_id, NULL)) {
+ /* Send packet to the server indicating that it cannot use this
+ connection as it has been replaced by backup router. */
+ SilcBuffer packet = silc_buffer_alloc(2);
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_REPLACED),
+ SILC_STR_UI_CHAR(0),
+ SILC_STR_END);
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_RESUME_ROUTER, 0,
+ packet->data, packet->len, TRUE);
+ silc_buffer_free(packet);
+
+ /* Mark the router disabled. The data sent earlier will go but nothing
+ after this does not go to this connection. */
+ idata->status |= SILC_IDLIST_STATUS_DISABLED;
+ } else {
+ /* If it is router announce our stuff to it. */
+ if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+ server->server_type == SILC_ROUTER) {
+ silc_server_announce_servers(server, FALSE, 0, sock);
+ silc_server_announce_clients(server, 0, sock);
+ silc_server_announce_channels(server, 0, sock);
+ }
+ }
+
return new_server;
}
{
SilcBuffer buffer = packet->buffer;
SilcIDList id_list;
- SilcServerEntry router;
+ SilcServerEntry router, server_entry;
SilcSocketConnection router_sock;
SilcIDPayload idp;
SilcIdType id_type;
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;
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)
+ server_entry = (SilcServerEntry)sock->user_data;
+ if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER &&
+ server_entry->server_type == SILC_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;
-
/* If the packet is coming from server then use the sender as the
origin of the the packet. If it came from router then check the real
sender of the packet and use that as the origin. */
if (sock->type == SILC_SOCKET_TYPE_SERVER) {
+ id_list = server->local_list;
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 &&
+ server->id_entry->server_type != SILC_BACKUP_ROUTER) {
+ id_list = server->global_list;
+ router_sock = server->router ? server->router->connection : sock;
+ }
} else {
void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
packet->src_id_type);
router = silc_idlist_find_server_by_id(server->global_list,
- sender_id, NULL);
+ sender_id, TRUE, NULL);
if (!router)
router = silc_idlist_find_server_by_id(server->local_list,
- sender_id, NULL);
+ 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:
{
SilcClientEntry entry;
+ /* Check that we do not have this client already */
+ entry = silc_idlist_find_client_by_id(server->global_list,
+ id, server->server_type,
+ NULL);
+ if (!entry)
+ entry = silc_idlist_find_client_by_id(server->local_list,
+ id, server->server_type,
+ NULL);
+ if (entry) {
+ SILC_LOG_DEBUG(("Ignoring client that we already have"));
+ goto out;
+ }
+
SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
silc_id_render(id, SILC_ID_CLIENT),
sock->type == SILC_SOCKET_TYPE_SERVER ?
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"));
+
+ /* Inform the sender that the ID is not usable */
+ silc_server_send_notify_signoff(server, sock, FALSE, id, NULL);
+ goto out;
+ }
entry->nickname = NULL;
- entry->data.registered = TRUE;
+ entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
if (sock->type == SILC_SOCKET_TYPE_SERVER)
server->stat.cell_clients++;
break;
case SILC_ID_SERVER:
- /* If the ID is mine, ignore it. */
- if (SILC_ID_SERVER_COMPARE(id, server->id)) {
- SILC_LOG_DEBUG(("Ignoring my own ID as new ID"));
- break;
- }
+ {
+ SilcServerEntry entry;
- 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));
+ /* If the ID is mine, ignore it. */
+ if (SILC_ID_SERVER_COMPARE(id, server->id)) {
+ SILC_LOG_DEBUG(("Ignoring my own ID as new ID"));
+ break;
+ }
- /* 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 the ID is the sender's ID, ignore it (we have it already) */
+ if (SILC_ID_SERVER_COMPARE(id, router->id)) {
+ SILC_LOG_DEBUG(("Ignoring sender's own ID"));
+ break;
+ }
+
+ /* Check that we do not have this server already */
+ entry = silc_idlist_find_server_by_id(server->global_list,
+ id, server->server_type,
+ NULL);
+ if (!entry)
+ entry = silc_idlist_find_server_by_id(server->local_list,
+ id, server->server_type,
+ NULL);
+ if (entry) {
+ SILC_LOG_DEBUG(("Ignoring server that we already have"));
+ goto out;
+ }
- if (sock->type == SILC_SOCKET_TYPE_SERVER)
- server->stat.cell_servers++;
- server->stat.servers++;
+ 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. */
+ entry = silc_idlist_add_server(id_list, NULL, 0, id, router,
+ router_sock);
+ if (!entry) {
+ SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
+ goto out;
+ }
+ entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+ if (sock->type == SILC_SOCKET_TYPE_SERVER)
+ server->stat.cell_servers++;
+ server->stat.servers++;
+ }
break;
case SILC_ID_CHANNEL:
SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
+ goto out;
break;
default:
+ goto out;
break;
}
+ /* 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);
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ packet->type, packet->flags,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
+ }
+
out:
silc_id_payload_free(idp);
}
{
SilcPacketContext *new_id;
SilcBuffer idp;
- uint16 id_len;
+ SilcUInt16 id_len;
SILC_LOG_DEBUG(("Processing New ID List"));
packet->type,
packet->flags | SILC_PACKET_FLAG_BROADCAST,
packet->buffer->data, packet->buffer->len, FALSE);
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ packet->type, packet->flags,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
}
/* Make copy of the original packet context, except for the actual
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;
SILC_LOG_DEBUG(("Processing New 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;
id = silc_channel_get_id(payload, &id_len);
+ server_entry = (SilcServerEntry)sock->user_data;
+
if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
/* Add the channel to global list as it is coming from router. It
cannot be our own channel as it is coming from router. */
- SILC_LOG_DEBUG(("New channel id(%s) from [Router] %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);
+ if (!channel) {
+ 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, 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++;
+ 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
we will add it to our local list. */
- SilcChannelEntry channel;
SilcBuffer chk;
- SILC_LOG_DEBUG(("New channel id(%s) from [Server] %s",
+ SILC_LOG_DEBUG(("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);
channel = silc_idlist_find_channel_by_name(server->global_list,
channel_name, NULL);
- /* 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
+ /* If the channel does not exist, then create it. This creates a new
key to the channel as well that we will send to the server. */
if (!channel) {
+ /* The protocol says that the Channel ID's IP address must be based
+ on the router's IP address. Check whether the ID is based in our
+ IP and if it is not then create a new ID and enforce the server
+ to switch the ID. */
+ if (server_entry->server_type != SILC_BACKUP_ROUTER &&
+ !SILC_ID_COMPARE(channel_id, server->id, server->id->ip.data_len)) {
+ SilcChannelID *tmp;
+ SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
+
+ if (silc_id_create_channel_id(server, server->id, server->rng, &tmp)) {
+ silc_server_send_notify_channel_change(server, sock, FALSE,
+ channel_id, tmp);
+ silc_free(channel_id);
+ channel_id = tmp;
+ }
+ }
+
+ /* Create the channel with the provided Channel ID */
channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
channel_name,
channel_id, FALSE);
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"));
packet->type,
packet->flags | SILC_PACKET_FLAG_BROADCAST,
packet->buffer->data, packet->buffer->len, FALSE);
+ silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+ packet->type, packet->flags,
+ packet->buffer->data, packet->buffer->len,
+ FALSE, TRUE);
}
/* Make copy of the original packet context, except for the actual
/* 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
actually be received at anytime but usually it is used only during
the connection authentication phase. Now, protocol says that this packet
can come from client or server, however, we support only this coming
- from client and expect that server's always knows what authentication
+ from client and expect that server always knows what authentication
method to use. */
void silc_server_connection_auth_request(SilcServer server,
SilcSocketConnection sock,
SilcPacketContext *packet)
{
- SilcServerConfigSectionClientConnection *client = NULL;
- uint16 conn_type;
+ SilcServerConfigClient *client = NULL;
+ SilcUInt16 conn_type;
int ret;
- SilcAuthMethod auth_meth;
+ SilcAuthMethod auth_meth = SILC_AUTH_NONE;
SILC_LOG_DEBUG(("Start"));
/* Get the authentication method for the client */
auth_meth = SILC_AUTH_NONE;
- client = silc_server_config_find_client_conn(server->config,
- sock->ip,
- sock->port);
+ client = silc_server_config_find_client(server, sock->ip);
if (!client)
- client = silc_server_config_find_client_conn(server->config,
- sock->hostname,
- sock->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);
+}