Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2007 Pekka Riikonen
+ Copyright (C) 1997 - 2009 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
SilcUInt32 tmp_len, tmp2_len;
SilcBool local, ret;
- if (idata->conn_type == SILC_CONN_CLIENT ||
- packet->src_id_type != SILC_ID_SERVER || !packet->dst_id) {
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ SILC_LOG_DEBUG(("Notify received from client, drop it"));
+ return;
+ }
+
+ if (packet->src_id_type != SILC_ID_SERVER){
+ SILC_LOG_DEBUG(("Bad notify packet received"));
+ return;
+ }
+
+ if (!packet->dst_id) {
SILC_LOG_DEBUG(("Bad notify packet received"));
return;
}
/* Parse the Notify Payload */
payload = silc_notify_payload_parse(buffer->data, silc_buffer_len(buffer));
- if (!payload)
+ if (!payload) {
+ SILC_LOG_DEBUG(("Marlformed notify 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
/* Packet is destined to channel */
if (!silc_id_str2id(packet->dst_id, packet->dst_id_len,
packet->dst_id_type, &channel_id,
- sizeof(channel_id)))
+ sizeof(channel_id))) {
+ SILC_LOG_DEBUG(("Malformed destination ID in notify packet"));
goto out;
+ }
silc_server_packet_send_dest(server, SILC_PRIMARY_ROUTE(server),
packet->type, packet->flags |
type = silc_notify_get_type(payload);
args = silc_notify_get_args(payload);
- if (!args)
+ if (!args) {
+ SILC_LOG_DEBUG(("Notify doesn't have any arguments, drop it"));
goto out;
+ }
switch(type) {
case SILC_NOTIFY_TYPE_JOIN:
channel = silc_idlist_find_channel_by_id(server->local_list,
SILC_ID_GET_ID(id), NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
goto out;
}
}
}
client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+ if (idata->conn_type == SILC_CONN_SERVER)
+ server->stat.cell_clients++;
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients + 1));
+ server->stat.clients++;
}
}
channel = silc_idlist_find_channel_by_id(server->local_list,
&channel_id, NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(&channel_id, SILC_ID_CHANNEL)));
goto out;
}
}
tmp_len = 128;
/* Update statistics */
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients - 1));
+ SILC_VERIFY(server->stat.clients > 0);
server->stat.clients--;
if (server->stat.cell_clients)
server->stat.cell_clients--;
/* Remove this client from watcher list if it is */
silc_server_del_from_watcher_list(server, client);
+ /* It's possible router doesn't accept our local client in the network
+ and sends SIGNOFF to our local client */
+ if (SILC_IS_LOCAL(client)) {
+ SILC_LOG_DEBUG(("SIGNOFF from router to local client, disconnect"));
+ if (client->data.sconn) {
+ silc_server_connection_free(client->data.sconn);
+ client->data.sconn = NULL;
+ }
+ silc_packet_set_context(client->connection, NULL);
+ silc_server_disconnect_remote(server, client->connection,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ "Router prevented registration");
+ }
+
client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
client->mode = 0;
client->router = NULL;
client->connection = NULL;
+ client->data.created = silc_time();
+ silc_dlist_del(server->expired_clients, client);
silc_dlist_add(server->expired_clients, client);
break;
channel = silc_idlist_find_channel_by_id(server->local_list,
&channel_id, NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(&channel_id, SILC_ID_CHANNEL)));
goto out;
}
}
channel = silc_idlist_find_channel_by_id(server->local_list,
&channel_id, NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(&channel_id, SILC_ID_CHANNEL)));
goto out;
}
}
channel = silc_idlist_find_channel_by_id(server->local_list,
&channel_id, NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(&channel_id, SILC_ID_CHANNEL)));
goto out;
}
}
channel = silc_idlist_find_channel_by_id(server->local_list,
SILC_ID_GET_ID(id), NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
goto out;
}
}
channel = silc_idlist_find_channel_by_id(server->global_list,
SILC_ID_GET_ID(id), NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
goto out;
}
}
/* Get client entry */
client = silc_idlist_find_client_by_id(server->global_list,
- SILC_ID_GET_ID(id),
+ SILC_ID_GET_ID(id2),
TRUE, &cache);
local = FALSE;
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
- SILC_ID_GET_ID(id),
+ SILC_ID_GET_ID(id2),
TRUE, &cache);
local = TRUE;
if (!client)
}
/* Update statistics */
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients - 1));
+ SILC_VERIFY(server->stat.clients > 0);
server->stat.clients--;
if (server->stat.cell_clients)
server->stat.cell_clients--;
silc_server_del_from_watcher_list(server, client);
/* Remove the client */
+ silc_dlist_del(server->expired_clients, client);
silc_idlist_del_data(client);
silc_idlist_del_client(local ? server->local_list :
server->global_list, client);
channel = silc_idlist_find_channel_by_id(server->local_list,
&channel_id, NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
goto out;
}
}
}
/* Update statistics */
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients - 1));
+ SILC_VERIFY(server->stat.clients > 0);
server->stat.clients--;
if (server->stat.cell_clients)
server->stat.cell_clients--;
SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
if (SILC_IS_LOCAL(client)) {
- server->stat.my_clients--;
+ if (!client->local_detached)
+ server->stat.my_clients--;
silc_schedule_task_del_by_context(server->schedule, client);
silc_idlist_del_data(client);
client->mode = 0;
client->mode = 0;
client->router = NULL;
client->connection = NULL;
+ client->data.created = silc_time();
+ silc_dlist_del(server->expired_clients, client);
silc_dlist_add(server->expired_clients, client);
break;
}
channel = silc_idlist_find_channel_by_id(server->local_list,
SILC_ID_GET_ID(id), NULL);
if (!channel) {
- SILC_LOG_DEBUG(("Notify for unknown channel"));
+ SILC_LOG_DEBUG(("Notify for unknown channel %s",
+ silc_id_render(SILC_ID_GET_ID(id), SILC_ID_CHANNEL)));
goto out;
}
}
silc_server_remove_from_channels(server, NULL, client, TRUE,
NULL, TRUE, FALSE);
+ silc_dlist_del(server->expired_clients, client);
silc_idlist_del_data(client);
silc_idlist_del_client(server->global_list, client);
}
break;
default:
+ SILC_LOG_DEBUG(("Unsupported notify %d", type));
break;
}
if (silc_buffer_unformat(buffer,
SILC_STR_UI16_NSTRING_ALLOC(&nickname,
&nickname_len),
- SILC_STR_END)) {
+ SILC_STR_END) >= 0) {
if (nickname_len > 128) {
nickname_len = 128;
nickname[nickname_len - 1] = '\0';
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);
+ silc_id_dup(&id, SILC_ID_CLIENT),
+ router, NULL);
if (!entry) {
SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
if (idata->conn_type == SILC_CONN_SERVER)
server->stat.cell_clients++;
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients + 1));
server->stat.clients++;
/* Check if anyone is watching this nickname */
/* 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,
+ entry = silc_idlist_add_server(id_list, NULL, 0,
+ silc_id_dup(&id, SILC_ID_SERVER), router,
router_sock);
if (!entry) {
SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
}
/* 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 =
+ silc_server_create_new_channel_with_id(
+ server, NULL, NULL,
+ channel_name,
+ silc_id_dup(&channel_id, SILC_ID_CHANNEL),
+ FALSE);
if (!channel) {
silc_channel_payload_free(payload);
return;
SilcPublicKey public_key;
const char *cipher, *hostname, *ip;
+ SILC_LOG_DEBUG(("Resuming client"));
+
silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
NULL, &hostname, &ip, NULL);
/* Take new keys and stuff into use in the old entry */
silc_idlist_del_data(detached_client);
silc_idlist_add_data(detached_client, idata);
+ idata->public_key = NULL;
if (detached_client->data.public_key) {
/* Add the resumed client's public key back to repository. */
detached_client->data.status &= ~SILC_IDLIST_STATUS_RESUME_RES;
detached_client->mode &= ~SILC_UMODE_DETACHED;
server->stat.my_detached--;
+ silc_dlist_del(server->expired_clients, detached_client);
/* We are finished - reset resuming client */
detached_client->resuming_client = NULL;
silc_server_check_watcher_list(server, detached_client, NULL,
SILC_NOTIFY_TYPE_UMODE_CHANGE);
- /* Delete this current client entry since we're resuming to old one. */
- server->stat.my_clients--;
+ /* Delete this current client entry since we're resuming to old one.
+ We decrement clients/cell_clients as we are getting rid of the
+ current client and replacing it with the detached one. We keep the
+ server user count as-is (incremented by the current client entry) as
+ we decremented the count already during detach, thus we'd be undoing
+ that operation. */
+ detached_client->local_detached = FALSE;
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients - 1));
+ SILC_VERIFY(server->stat.clients > 0);
server->stat.clients--;
if (server->stat.cell_clients)
server->stat.cell_clients--;
silc_server_remove_from_channels(server, NULL, client, FALSE,
NULL, FALSE, FALSE);
silc_server_del_from_watcher_list(server, client);
+ silc_dlist_del(server->expired_clients, client);
if (!silc_idlist_del_client(server->local_list, client))
silc_idlist_del_client(server->global_list, client);
client = detached_client;
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,
- NULL))
- silc_idcache_del_by_context(server->global_list->clients, client, NULL);
- silc_free(client->id);
- *client->id = client_id;
- silc_idcache_add(server->local_list->clients, nicknamec,
- client->id, client);
+ /* Update entry */
+ if (!silc_idcache_update_by_context(server->local_list->clients, client,
+ &client_id, NULL, FALSE))
+ silc_idcache_update_by_context(server->global_list->clients, client,
+ &client_id, NULL, FALSE);
+
+ /* Move entry to local list if it is in global list */
+ if (silc_idcache_find_by_context(server->global_list->clients, client,
+ &id_cache))
+ silc_idcache_move(server->global_list->clients,
+ server->local_list->clients, id_cache);
/* Send some nice info to the client */
silc_server_send_connect_notifys(server, sock, client);
FALSE, TRUE);
}
+ /* If the client has a locally-connected previous owner, then we
+ will need to notify them that the resume has completed. Note
+ that if the previous owner was a router, this case is already
+ handled above by the broadcast, so we shouldn't attempt to
+ send another notification in that case. Additionally, if
+ the previous owner was the server that sent the packet, then
+ we'll not send the notification as it will have already done
+ the necessary work locally. */
+ if (server->server_type == SILC_ROUTER &&
+ idata->conn_type == SILC_CONN_SERVER &&
+ detached_client->router &&
+ SILC_IS_LOCAL(detached_client->router) &&
+ detached_client->router->server_type != SILC_ROUTER)
+ silc_server_packet_send(server, detached_client->router->connection,
+ SILC_PACKET_RESUME_CLIENT, 0,
+ buffer->data, silc_buffer_len(buffer));
+
/* Client is detached, and now it is resumed. Remove the detached
mode and mark that it is resumed. */
server_entry->server_type == SILC_ROUTER)
local = FALSE;
- /* Change the client to correct list. */
- if (!silc_idcache_del_by_context(server->local_list->clients,
- detached_client, NULL))
- silc_idcache_del_by_context(server->global_list->clients,
- detached_client, NULL);
- silc_idcache_add(local && server->server_type == SILC_ROUTER ?
- server->local_list->clients :
- server->global_list->clients, nicknamec,
- detached_client->id, detached_client);
+ /* Move entry to correct list */
+ if (local && server->server_type == SILC_ROUTER) {
+ if (silc_idcache_find_by_context(server->global_list->clients,
+ detached_client, &id_cache))
+ silc_idcache_move(server->global_list->clients,
+ server->local_list->clients, id_cache);
+ } else {
+ if (silc_idcache_find_by_context(server->local_list->clients,
+ detached_client, &id_cache))
+ silc_idcache_move(server->local_list->clients,
+ server->global_list->clients, id_cache);
+ }
+ /* We don't own this client anymore, if we ever did, as we were just
+ * told that someone else resumed it. Thus, it is most definitely no
+ * a detached client.*/
+ detached_client->local_detached = FALSE;
/* Change the owner of the client */
detached_client->router = server_entry;