SilcArgumentPayload args;
SilcChannelID *channel_id, *channel_id2;
SilcClientID *client_id, *client_id2;
+ SilcServerID *server_id;
SilcChannelEntry channel;
SilcClientEntry client;
+ SilcServerEntry server_entry;
SilcChannelClientEntry chl;
SilcIDCacheEntry cache;
- unsigned int mode;
+ uint32 mode;
unsigned char *tmp;
- unsigned int tmp_len;
+ uint32 tmp_len;
SILC_LOG_DEBUG(("Start"));
if (dst_sock)
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac, packet, TRUE);
+ idata->hmac_receive, packet, TRUE);
}
/* If we are router and this packet is not already broadcast packet
if (tmp_len > 128)
tmp = NULL;
- /* Remove the client from all channels */
- silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, TRUE);
+ /* Remove the client from all channels. */
+ silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
client->data.registered = FALSE;
cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
-
-#if 0
- /* Remove the client entry */
- if (!silc_idlist_del_client(server->global_list, client))
- silc_idlist_del_client(server->local_list, client);
-#endif
break;
case SILC_NOTIFY_TYPE_TOPIC_SET:
/* Get entry to the channel user list */
silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ SilcChannelClientEntry chl2 = NULL;
+
+ /* If the mode is channel founder and we already find a client
+ to have that mode on the channel we will enforce the sender
+ to change the channel founder mode away. There can be only one
+ channel founder on the channel. */
+ if (server->server_type == SILC_ROUTER &&
+ mode & SILC_CHANNEL_UMODE_CHANFO &&
+ chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ silc_server_send_notify_cumode(server, sock, FALSE, channel,
+ (mode & (~SILC_CHANNEL_UMODE_CHANFO)),
+ server->id, SILC_ID_SERVER,
+ client->id);
+ silc_free(channel_id);
+
+ /* Change the mode back if we changed it */
+ if (chl2)
+ chl2->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+ goto out;
+ }
+
if (chl->client == client) {
/* Change the mode */
chl->mode = mode;
- break;
+ if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+ break;
+
+ chl2 = chl;
}
+ }
/* Send the same notify to the channel */
silc_server_packet_send_to_channel(server, sock, channel, packet->type,
}
if (channel_id2) {
- SilcBuffer users = NULL;
+ SilcBuffer users = NULL, users_modes = NULL;
/* Re-announce our clients on the channel as the ID has changed now */
- silc_server_announce_get_channel_users(server, channel, &users);
+ silc_server_announce_get_channel_users(server, channel, &users,
+ &users_modes);
if (users) {
silc_buffer_push(users, users->data - users->head);
silc_server_packet_send(server, sock,
users->data, users->len, FALSE);
silc_buffer_free(users);
}
+ if (users_modes) {
+ silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ users_modes->data, users_modes->len, FALSE);
+ silc_buffer_free(users_modes);
+ }
}
silc_free(channel_id);
break;
case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
- SILC_LOG_DEBUG(("SERVER SIGNOFF notify (not-impl XXX)"));
+ /*
+ * Remove the server entry and all clients that this server owns.
+ */
+
+ SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
+
+ /* Get Server 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);
+ if (!server_id)
+ goto out;
+
+ /* Get server entry */
+ server_entry = silc_idlist_find_server_by_id(server->global_list,
+ server_id, NULL);
+ if (!server_entry) {
+ server_entry = silc_idlist_find_server_by_id(server->local_list,
+ server_id, NULL);
+ if (!server_entry) {
+ silc_free(server_id);
+ goto out;
+ }
+ }
+ silc_free(server_id);
+
+ /* Free all client entries that this server owns as they will
+ become invalid now as well. */
+ 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);
+
+ /* XXX update statistics */
+
break;
case SILC_NOTIFY_TYPE_KICKED:
* Distribute the notify to local clients on channels
*/
unsigned char *id;
- unsigned int id_len;
+ uint32 id_len;
SILC_LOG_DEBUG(("KILLED notify"));
{
SilcPacketContext *new;
SilcBuffer buffer;
- unsigned short len;
+ uint16 len;
- SILC_LOG_DEBUG(("Processing New Notify List"));
+ SILC_LOG_DEBUG(("Processing Notify List"));
if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
packet->src_id_type != SILC_ID_SERVER)
/* Send the private message */
silc_server_send_private_message(server, dst_sock, idata->send_key,
- idata->hmac, packet);
+ idata->hmac_send, packet);
}
/* Received private message key packet.. This packet is never for us. It is to
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac, packet, FALSE);
+ idata->hmac_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 (SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
+ if (memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
return;
}
idata = (SilcIDListData)client;
/* Encrypt packet */
- silc_packet_encrypt(idata->send_key, idata->hmac, dst_sock->outbuf,
+ silc_packet_encrypt(idata->send_key, idata->hmac_send, dst_sock->outbuf,
buffer->len);
/* Send the packet */
SilcChannelClientEntry chl;
SilcChannelID *id = NULL;
void *sender = NULL;
+ void *sender_entry = NULL;
SILC_LOG_DEBUG(("Processing channel message"));
packet->src_id_type);
if (!sender)
goto out;
- if (sock->type != SILC_SOCKET_TYPE_ROUTER &&
- packet->src_id_type == SILC_ID_CLIENT) {
+ if (packet->src_id_type == SILC_ID_CLIENT) {
silc_list_start(channel->user_list);
while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (chl->client && !SILC_ID_CLIENT_COMPARE(chl->client->id, sender))
+ if (chl->client && SILC_ID_CLIENT_COMPARE(chl->client->id, sender)) {
+ sender_entry = chl->client;
break;
+ }
}
if (chl == SILC_LIST_END) {
SILC_LOG_DEBUG(("Client not on channel"));
}
}
- /* If we are router and the packet came from router and private key
- has not been set for the channel then we must encrypt the packet
- as it was decrypted with the session key shared between us and the
- router which sent it. This is so, because cells does not share the
- same channel key */
- if (server->server_type == SILC_ROUTER &&
- sock->type == SILC_SOCKET_TYPE_ROUTER &&
- !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
- SilcBuffer chp;
- unsigned int iv_len, i;
- unsigned short data_len, flags;
-
- iv_len = silc_cipher_get_block_len(channel->channel_key);
- if (channel->iv[0] == '\0')
- for (i = 0; i < iv_len; i++) channel->iv[i] =
- silc_rng_get_byte(server->rng);
- else
- silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
-
- /* Encode new payload. This encrypts it also. */
- SILC_GET16_MSB(flags, packet->buffer->data);
- SILC_GET16_MSB(data_len, packet->buffer->data + 2);
- chp = silc_channel_message_payload_encode(flags, data_len,
- packet->buffer->data + 4,
- iv_len, channel->iv,
- channel->channel_key,
- channel->hmac);
- silc_buffer_put(packet->buffer, chp->data, chp->len);
- silc_buffer_free(chp);
- }
-
/* Distribute the packet to our local clients. This will send the
packet for further routing as well, if needed. */
silc_server_packet_relay_to_channel(server, sock, channel, sender,
- packet->src_id_type,
+ packet->src_id_type, sender_entry,
packet->buffer->data,
packet->buffer->len, FALSE);
SilcBuffer reply;
SilcIDListData idata;
char *username = NULL, *realname = NULL, *id_string;
+ uint32 id_len;
int ret;
SILC_LOG_DEBUG(("Creating new client"));
if (!silc_idcache_find_by_context(server->local_list->clients,
sock->user_data, &cache)) {
SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Unknown client");
return NULL;
}
silc_free(username);
if (realname)
silc_free(realname);
+ silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ "Incomplete client information");
return NULL;
}
client->username = username;
client->userinfo = realname ? realname : strdup(" ");
client->id = client_id;
+ id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
/* Update the cache entry */
cache->id = (void *)client_id;
cache->type = SILC_ID_CLIENT;
cache->data = username;
+ cache->data_len = strlen(username);
silc_idcache_sort_by_data(server->local_list->clients);
/* Notify our router about new client on the SILC network */
silc_server_send_new_id(server, (SilcSocketConnection)
server->router->connection,
server->server_type == SILC_ROUTER ? TRUE : FALSE,
- client->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+ 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 + SILC_ID_CLIENT_LEN);
+ 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(SILC_ID_CLIENT_LEN),
- SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
+ 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);
SilcServerID *server_id;
SilcIDListData idata;
unsigned char *server_name, *id_string;
- unsigned short id_len, name_len;
+ uint16 id_len, name_len;
int ret;
SILC_LOG_DEBUG(("Creating new server"));
server->router->connection != sock)
silc_server_send_new_id(server, server->router->connection,
TRUE, new_server->id, SILC_ID_SERVER,
- SILC_ID_SERVER_LEN);
+ silc_id_get_len(server_id, SILC_ID_SERVER));
if (server->server_type == SILC_ROUTER)
server->stat.cell_servers++;
else
id_list = server->global_list;
- router_sock = sock;
- router = sock->user_data;
+ /* 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) {
+ router_sock = sock;
+ router = sock->user_data;
+ } 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);
+ if (!router)
+ router = silc_idlist_find_server_by_id(server->local_list,
+ sender_id, NULL);
+ silc_free(sender_id);
+ if (!router)
+ goto out;
+ router_sock = sock;
+ }
switch(id_type) {
case SILC_ID_CLIENT:
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;
+ }
+
SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
silc_id_render(id, SILC_ID_SERVER),
sock->type == SILC_SOCKET_TYPE_SERVER ?
"Server" : "Router", sock->hostname));
-
+
/* As a router we keep information of all global information in our global
list. Cell wide information however is kept in the local list. */
silc_idlist_add_server(id_list, NULL, 0, id, router, router_sock);
{
SilcPacketContext *new_id;
SilcBuffer idp;
- unsigned short id_len;
+ uint16 id_len;
SILC_LOG_DEBUG(("Processing New ID List"));
SilcChannelPayload payload;
SilcChannelID *channel_id;
char *channel_name;
- unsigned int name_len;
+ uint32 name_len;
unsigned char *id;
- unsigned int id_len;
- unsigned int mode;
+ uint32 id_len;
+ uint32 mode;
SILC_LOG_DEBUG(("Processing New Channel"));
/* The channel exist by that name, check whether the ID's match.
If they don't then we'll force the server to use the ID we have.
We also create a new key for the channel. */
- SilcBuffer users = NULL;
+ SilcBuffer users = NULL, users_modes = NULL;
- if (SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
+ 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_LOG_DEBUG(("Forcing the server to change Channel ID"));
silc_server_send_notify_channel_change(server, sock, FALSE,
- channel_id,
- channel->id,
- SILC_ID_CHANNEL_LEN);
+ channel_id, channel->id);
}
/* If the mode is different from what we have then enforce the
SILC_LOG_DEBUG(("Forcing the server to change channel mode"));
silc_server_send_notify_cmode(server, sock, FALSE, channel,
channel->mode, server->id,
- SILC_ID_SERVER, SILC_ID_SERVER_LEN,
+ SILC_ID_SERVER,
channel->cipher, channel->hmac_name);
}
/* Since the channel is coming from server and we also know about it
then send the JOIN notify to the server so that it see's our
users on the channel "joining" the channel. */
- silc_server_announce_get_channel_users(server, channel, &users);
+ silc_server_announce_get_channel_users(server, channel, &users,
+ &users_modes);
if (users) {
silc_buffer_push(users, users->data - users->head);
silc_server_packet_send(server, sock,
users->data, users->len, FALSE);
silc_buffer_free(users);
}
+ if (users_modes) {
+ silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ users_modes->data, users_modes->len, FALSE);
+ silc_buffer_free(users_modes);
+ }
}
}
{
SilcPacketContext *new;
SilcBuffer buffer;
- unsigned short len1, len2;
+ uint16 len1, len2;
SILC_LOG_DEBUG(("Processing New Channel List"));
/* Relay the packet */
silc_server_relay_packet(server, dst_sock, idata->send_key,
- idata->hmac, packet, FALSE);
+ idata->hmac_send, packet, FALSE);
}
/* Received connection auth request packet that is used during connection
SilcPacketContext *packet)
{
SilcServerConfigSectionClientConnection *client = NULL;
- unsigned short conn_type;
+ uint16 conn_type;
int ret;
SilcAuthMethod auth_meth;
conn_type,
auth_meth);
}
+
+/* Received REKEY packet. The sender of the packet wants to regenerate
+ its session keys. This starts the REKEY protocol. */
+
+void silc_server_rekey(SilcServer server,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcProtocol protocol;
+ SilcServerRekeyInternalContext *proto_ctx;
+ SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Allocate internal protocol context. This is sent as context
+ to the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = (void *)server;
+ proto_ctx->sock = sock;
+ proto_ctx->responder = TRUE;
+ proto_ctx->pfs = idata->rekey->pfs;
+
+ /* Perform rekey protocol. Will call the final callback after the
+ protocol is over. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY,
+ &protocol, proto_ctx, silc_server_rekey_final);
+ sock->protocol = protocol;
+
+ if (proto_ctx->pfs == FALSE)
+ /* Run the protocol */
+ protocol->execute(server->timeout_queue, 0, protocol, sock->sock, 0, 0);
+}