{
if (entry) {
/* Remove from cache */
- if (!silc_idcache_del_by_context(id_list->servers, entry))
+ if (!silc_idcache_del_by_context(id_list->servers, entry)) {
+ SILC_LOG_DEBUG(("Unknown server, did not delete"));
return FALSE;
+ }
SILC_LOG_DEBUG(("Deleting server %s id %s", entry->server_name ?
entry->server_name : "",
if (entry) {
/* Remove from cache */
- if (!silc_idcache_del_by_context(id_list->clients, entry))
+ if (!silc_idcache_del_by_context(id_list->clients, entry)) {
+ SILC_LOG_DEBUG(("Unknown client, did not delete"));
return FALSE;
+ }
assert(!silc_hash_table_count(entry->channels));
{
if (entry) {
/* Remove from cache */
- if (!silc_idcache_del_by_context(id_list->channels, entry))
+ if (!silc_idcache_del_by_context(id_list->channels, entry)) {
+ SILC_LOG_DEBUG(("Unknown channel, did not delete"));
return FALSE;
+ }
SILC_LOG_DEBUG(("Deleting channel %s", entry->channel_name));
/* Get client entry */
client = silc_idlist_find_client_by_id(server->global_list,
client_id, TRUE, &cache);
- local = TRUE;
+ local = FALSE;
if (!client) {
client = silc_idlist_find_client_by_id(server->local_list,
client_id, TRUE, &cache);
- local = FALSE;
+ local = TRUE;
if (!client) {
silc_free(client_id);
continue;
server_id, TRUE, NULL);
if (server_entry) {
if (SILC_IS_LOCAL(server_entry)) {
- silc_server_disconnect_remote(server, server_entry->connection,
+ silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_OPERATION_ALLOWED,
"Too many registrations");
- if (((SilcSocketConnection)server_entry->connection)->user_data)
+ if (sock->user_data)
silc_server_free_sock_user_data(server, sock, NULL);
+ return NULL;
} else {
silc_idcache_del_by_context(server->local_list->servers, server_entry);
}
server_id, TRUE, NULL);
if (server_entry) {
if (SILC_IS_LOCAL(server_entry)) {
- silc_server_disconnect_remote(server, server_entry->connection,
+ silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_OPERATION_ALLOWED,
"Too many registrations");
- if (((SilcSocketConnection)server_entry->connection)->user_data)
- silc_server_free_sock_user_data(server, server_entry->connection,
- NULL);
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return NULL;
} else {
silc_idcache_del_by_context(server->global_list->servers,
server_entry);
to the protocol. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
proto_ctx->server = (void *)server;
- proto_ctx->sock = sock;
+ proto_ctx->sock = silc_socket_dup(sock);
proto_ctx->responder = TRUE;
proto_ctx->pfs = idata->rekey->pfs;
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2002 Pekka Riikonen
+ Copyright (C) 1997 - 2003 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
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
+ the Free Software Foundation; version 2 of the License.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
bool pfs; /* TRUE if PFS is to be used */
SilcSKE ske; /* Defined if PFS is used */
SilcPacketContext *packet;
+ SilcTask timeout_task;
} SilcServerRekeyInternalContext;
/* Prototypes */
server->router = id_entry;
server->router->server_type = SILC_ROUTER;
server->standalone = FALSE;
+ server->backup_primary = FALSE;
/* If we are router then announce our possible servers. Backup
router announces also global servers. */
if (server->router == user_data) {
/* Check whether we have a backup router connection */
if (!backup_router || backup_router == user_data) {
- silc_server_create_connections(server);
+ if (!server->no_reconnect)
+ silc_server_create_connections(server);
server->id_entry->router = NULL;
server->router = NULL;
server->standalone = TRUE;
} else if (server->server_type == SILC_SERVER &&
sock->type == SILC_SOCKET_TYPE_ROUTER) {
/* Reconnect to the router (backup) */
- silc_server_create_connections(server);
+ if (!server->no_reconnect)
+ silc_server_create_connections(server);
}
if (user_data->server_name)
if (!client)
return;
- SILC_LOG_DEBUG(("Removing client from joined channels"));
-
if (notify && !client->id)
notify = FALSE;
+ SILC_LOG_DEBUG(("Removing client %s from joined channels",
+ notify ? silc_id_render(client->id, SILC_ID_CLIENT) : ""));
+
if (notify) {
clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
if (!clidp)
return buffer;
}
+/* Timeout callback for unsuccessful rekey. The rekey did not go through
+ for some reason. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_timeout)
+{
+ SilcServerRekeyInternalContext *ctx = context;
+ SilcServer server = app_context;
+ SilcSocketConnection sock = ctx->sock;
+
+ SILC_LOG_DEBUG(("Timeout occurred in rekey protocol with %s:%d [%s]",
+ sock->hostname, sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
+
+ SILC_LOG_WARNING(("Timeout occurred in rekey protocol with %s:%d [%s]",
+ sock->hostname, sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
+
+ if (sock->protocol) {
+ silc_protocol_cancel(sock->protocol, server->schedule);
+ silc_protocol_free(sock->protocol);
+ sock->protocol = NULL;
+ }
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ silc_socket_free(sock);
+ silc_free(ctx);
+}
+
/* A timeout callback for the re-key. We will be the initiator of the
re-key protocol. */
SilcProtocol protocol;
SilcServerRekeyInternalContext *proto_ctx;
+ /* Do not execute rekey with disabled connections, as it would not
+ go through anyway. */
+ if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+ return;
+
/* If rekey protocol is active already wait for it to finish */
if (sock->protocol && sock->protocol->protocol &&
sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)
return;
}
- SILC_LOG_DEBUG(("Executing rekey protocol"));
+ SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s]",
+ sock->hostname, sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
/* 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->sock = silc_socket_dup(sock);
proto_ctx->responder = FALSE;
proto_ctx->pfs = idata->rekey->pfs;
&protocol, proto_ctx, silc_server_rekey_final);
sock->protocol = protocol;
+ /* Register timeout callback in case the rekey does not go through. */
+ proto_ctx->timeout_task =
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rekey_timeout,
+ proto_ctx,
+ server->config->key_exchange_timeout, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
/* Run the protocol */
silc_protocol_execute(protocol, server->schedule, 0, 0);
}
SilcSocketConnection sock = ctx->sock;
SilcIDListData idata = (SilcIDListData)sock->user_data;
+ if (ctx->timeout_task)
+ silc_schedule_task_del(server->schedule, ctx->timeout_task);
+
if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
+ silc_socket_free(sock);
silc_free(ctx);
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
- SILC_LOG_DEBUG(("Rekey protocol completed"));
+ SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]",
+ sock->hostname, sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
/* Purge the outgoing data queue to assure that all rekey packets really
go to the network before we quit the protocol. */
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
+ silc_socket_free(sock);
silc_free(ctx);
}
typedef struct {
SilcServer server;
SilcSocketConnection sock;
- bool responder;
SilcUInt8 type;
SilcUInt8 session;
SilcServerBackupProtocolSession *sessions;
SilcUInt32 sessions_count;
long start;
+ unsigned int responder : 1;
+ unsigned int received_failure : 1;
+ unsigned int timeout : 1;
} *SilcServerBackupProtocolContext;
SILC_TASK_CALLBACK(silc_server_backup_timeout)
{
SilcProtocol protocol = context;
+ SilcServerBackupProtocolContext ctx = protocol->context;
SilcServer server = app_context;
SILC_LOG_INFO(("Timeout occurred during backup resuming protocol"));
+ ctx->timeout = TRUE;
silc_protocol_cancel(protocol, server->schedule);
protocol->state = SILC_PROTOCOL_STATE_ERROR;
silc_protocol_execute_final(protocol, server->schedule);
/* If other protocol is executing at the same time, start with timeout. */
if (sock->protocol) {
+ SILC_LOG_DEBUG(("Other protocol is executing, wait for it to finish"));
silc_schedule_task_add(server->schedule, sock->sock,
silc_server_backup_responder_start,
proto_ctx, 2, 0,
SILC_TASK_PRI_NORMAL);
}
+/* Callback to send START_USE to backup to check whether using backup
+ is ok. */
+
+SILC_TASK_CALLBACK(silc_server_backup_check_status)
+{
+ SilcSocketConnection sock = context;
+ SilcServer server = app_context;
+
+ /* Check whether we are still using backup */
+ if (!server->backup_primary)
+ return;
+
+ silc_server_backup_send_start_use(server, sock, FALSE);
+ silc_socket_free(sock); /* unref */
+}
+
typedef struct {
SilcServer server;
SilcSocketConnection sock;
/* If running other protocol already run this one a bit later. */
if (sock->protocol) {
+ SILC_LOG_DEBUG(("Other protocol is running, wait for it to finish"));
silc_schedule_task_add(server->schedule, 0,
silc_server_backup_connected_later,
proto_ctx, 10, 0,
if (server->server_type == SILC_SERVER)
silc_server_update_channels_by_server(server, backup_router, router);
silc_server_backup_replaced_del(server, backup_router);
-
- /* Announce all of our information to the router. */
- if (server->server_type == SILC_ROUTER)
- silc_server_announce_servers(server, FALSE, ctx->start, sock);
-
- /* Announce our clients and channels to the router */
- silc_server_announce_clients(server, ctx->start, sock);
- silc_server_announce_channels(server, ctx->start, sock);
}
/* Send notify about primary router going down to local operators */
case SILC_PROTOCOL_STATE_FAILURE:
/* Protocol has ended, call the final callback */
SILC_LOG_ERROR(("Error during backup resume: received Failure"));
+ ctx->received_failure = TRUE;
if (protocol->final_callback)
silc_protocol_execute_final(protocol, server->schedule);
else
sock->protocol = NULL;
if (error) {
- /* If we are server close all router connections except backup,
- send confirmation to backup that using it is still ok and continue
- sending traffic there. The backup will reply with error if
- it's not ok. */
+
if (server->server_type == SILC_SERVER &&
- server_entry->server_type == SILC_ROUTER) {
- server->backup_noswitch = TRUE;
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock, NULL);
- silc_server_disconnect_remote(server, sock, 0, NULL);
- server->backup_noswitch = FALSE;
-
- /* Send START_USE just in case using backup wouldn't be ok. */
- silc_server_backup_send_start_use(server, server->router->connection,
- FALSE);
-
- silc_server_create_connections(server);
+ server_entry->server_type == SILC_ROUTER)
continue;
- }
- /* If error occurred and we are backup router, we close connections. */
+ /* Backup router */
if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router) {
- server->backup_noswitch = TRUE;
- server->server_type = SILC_BACKUP_ROUTER;
if (ctx->sock == sock) {
silc_socket_free(sock); /* unref */
ctx->sock = NULL;
}
- server->backup_noswitch = TRUE;
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock, NULL);
- silc_server_disconnect_remote(server, sock, 0, NULL);
- server->backup_noswitch = FALSE;
+ if (!ctx->received_failure) {
+ /* Protocol error, probably timeout. Just restart the protocol. */
+ SilcServerBackupProtocolContext proto_ctx;
+
+ /* Restart the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = server;
+ proto_ctx->sock = silc_socket_dup(sock);
+ proto_ctx->responder = FALSE;
+ proto_ctx->type = SILC_SERVER_BACKUP_START;
+ proto_ctx->start = time(0);
+
+ /* Start through scheduler */
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_backup_connected_later,
+ proto_ctx, 2, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ } else {
+ /* If failure was received, switch back to normal backup router.
+ For some reason primary wouldn't accept that we were supposed
+ to perfom resuming protocol. */
+ server->server_type = SILC_BACKUP_ROUTER;
+ silc_server_local_servers_toggle_enabled(server, FALSE);
+ silc_server_update_servers_by_server(server, server->id_entry,
+ sock->user_data);
+ silc_server_update_clients_by_server(server, NULL,
+ sock->user_data, FALSE);
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, ctx->start, sock);
+ silc_server_announce_channels(server, ctx->start, sock);
+ }
- silc_server_create_connections(server);
continue;
}
}
}
}
- if (!error)
+ if (!error) {
SILC_LOG_INFO(("Backup resuming protocol ended successfully"));
+ if (ctx->type == SILC_SERVER_BACKUP_RESUMED && server->router) {
+ /* Announce all of our information to the router. */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_announce_servers(server, FALSE, ctx->start,
+ server->router->connection);
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, ctx->start,
+ server->router->connection);
+ silc_server_announce_channels(server, ctx->start,
+ server->router->connection);
+ }
+ } else {
+ /* Error */
+
+ if (server->server_type == SILC_SERVER) {
+ /* If we are still using backup router Send confirmation to backup
+ that using it is still ok and continue sending traffic there.
+ The backup will reply with error if it's not ok. */
+ if (server->router && server->backup_primary) {
+ /* Send START_USE just in case using backup wouldn't be ok. */
+ silc_server_backup_send_start_use(server, server->router->connection,
+ FALSE);
+
+ /* Check couple of times same START_USE just in case. */
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_backup_check_status,
+ silc_socket_dup(server->router->connection),
+ 5, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_backup_check_status,
+ silc_socket_dup(server->router->connection),
+ 20, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_backup_check_status,
+ silc_socket_dup(server->router->connection),
+ 60, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ }
+ }
+ }
+
if (ctx->sock && ctx->sock->protocol)
ctx->sock->protocol = NULL;
if (ctx->sock)
unsigned int wait_backup : 1; /* Set if we are waiting for backup
router to connect to us. */
unsigned int server_shutdown: 1; /* Set when shutting down */
+ unsigned int no_reconnect : 1; /* If set, server won't reconnect to
+ router after disconnection. */
SilcServerEntry router; /* Pointer to the primary router */
unsigned long router_connect; /* Time when router was connected */
whether this server is in our cell, but not connected to
us (in which case we must remove it). */
- if (server_entry->router == from) {
+ if (from) {
+ if (server_entry->router == from) {
+ SILC_LOG_DEBUG(("Updating server (local) %s",
+ server_entry->server_name ?
+ server_entry->server_name : ""));
+ server_entry->router = to;
+ server_entry->connection = to->connection;
+ }
+ } else {
+ /* Update all */
SILC_LOG_DEBUG(("Updating server (local) %s",
server_entry->server_name ?
server_entry->server_name : ""));
whether this server is in our cell, but not connected to
us (in which case we must remove it). */
- if (server_entry->router == from) {
+ if (from) {
+ if (server_entry->router == from) {
+ SILC_LOG_DEBUG(("Updating server (global) %s",
+ server_entry->server_name ?
+ server_entry->server_name : ""));
+ server_entry->router = to;
+ server_entry->connection = to->connection;
+ }
+ } else {
+ /* Update all */
SILC_LOG_DEBUG(("Updating server (global) %s",
server_entry->server_name ?
server_entry->server_name : ""));
if (silc_idcache_list_first(list, &id_cache)) {
while (id_cache) {
channel = (SilcChannelEntry)id_cache->context;
- if (channel->router == from)
+ if (from) {
+ if (channel->router == from)
+ channel->router = to;
+ } else {
+ /* Update all */
channel->router = to;
+ }
if (!silc_idcache_list_next(list, &id_cache))
break;
}
if (type == 1) {
/* Invite string. Get the old invite string from hash table
and append this at the end of the existing one. */
- if (!silc_hash_table_find(list, (void *)1, NULL, (void **)&tmp2)) {
+ if (!silc_hash_table_find(list, (void *)1, NULL, (void *)&tmp2)) {
tmp2 = silc_calloc(1, sizeof(*tmp2));
silc_hash_table_add(list, (void *)1, tmp2);
}