Merged from silc_1_0_branch.
[silc.git] / apps / silcd / server_backup.c
index a3aaf17e888183828e3a862e9c8f3346f737f625..78634fe2e02fcc35a8b58da88df56808590256ce 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 Pekka Riikonen
+  Copyright (C) 2001 - 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
   GNU General Public License for more details.
 
 */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 
 SILC_TASK_CALLBACK(silc_server_protocol_backup_done);
+static void silc_server_backup_connect_primary(SilcServer server,
+                                              SilcServerEntry server_entry,
+                                              void *context);
 
 /* Backup router */
 typedef struct {
   SilcServerEntry server;
+  SilcIDIP ip;
+  SilcUInt16 port;
   bool local;
 } SilcServerBackupEntry;
 
@@ -32,20 +38,20 @@ typedef struct {
    by backup router. */
 typedef struct {
   SilcIDIP ip;
-  uint16 port;
+  SilcUInt16 port;
   SilcServerEntry server;      /* Backup router that replaced the primary */
 } SilcServerBackupReplaced;
 
 /* Backup context */
 struct SilcServerBackupStruct {
   SilcServerBackupEntry *servers;
-  uint32 servers_count;
+  SilcUInt32 servers_count;
   SilcServerBackupReplaced **replaced;
-  uint32 replaced_count;
+  SilcUInt32 replaced_count;
 };
 
 typedef struct {
-  uint8 session;
+  SilcUInt8 session;
   bool connected;
   SilcServerEntry server_entry;
 } SilcServerBackupProtocolSession;
@@ -55,41 +61,46 @@ typedef struct {
   SilcServer server;
   SilcSocketConnection sock;
   bool responder;
-  uint8 type;
-  uint8 session;
+  SilcUInt8 type;
+  SilcUInt8 session;
   SilcServerBackupProtocolSession *sessions;
-  uint32 sessions_count;
+  SilcUInt32 sessions_count;
   long start;
 } *SilcServerBackupProtocolContext;
 
-/* Backup resuming protocol types */
-#define SILC_SERVER_BACKUP_START            1
-#define SILC_SERVER_BACKUP_START_GLOBAL     2
-#define SILC_SERVER_BACKUP_CONNECTED        3
-#define SILC_SERVER_BACKUP_ENDING           4
-#define SILC_SERVER_BACKUP_RESUMED          5
-#define SILC_SERVER_BACKUP_RESUMED_GLOBAL   6
-#define SILC_SERVER_BACKUP_REPLACED         20
-
-/* Sets the `backup_server' to be one of our backup router. This can be
-   called multiple times to set multiple backup routers. If `local' is
-   TRUE then the `backup_server' is in the local cell, if FALSE it is
-   in some other cell. */
+/* Adds the `backup_server' to be one of our backup router. This can be
+   called multiple times to set multiple backup routers. The `ip' and `port'
+   is the IP and port that the `backup_router' will replace if the `ip'
+   will become unresponsive. If `local' is TRUE then the `backup_server' is
+   in the local cell, if FALSE it is in some other cell. */
 
 void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
-                           bool local)
+                           const char *ip, int port, bool local)
 {
   int i;
 
+  if (!ip)
+    return;
+
   if (!server->backup) {
     server->backup = silc_calloc(1, sizeof(*server->backup));
-    server->backup->servers_count++;
+    if (!server->backup)
+      return;
   }
 
+  SILC_LOG_DEBUG(("Backup router %s will replace %s",
+                 ((SilcSocketConnection)backup_server->connection)->ip,
+                 ip, port));
+
   for (i = 0; i < server->backup->servers_count; i++) {
     if (!server->backup->servers[i].server) {
       server->backup->servers[i].server = backup_server;
       server->backup->servers[i].local = local;
+      server->backup->servers[i].port = htons(port);
+      memset(server->backup->servers[i].ip.data, 0,
+            sizeof(server->backup->servers[i].ip.data));
+      silc_net_addr2bin(ip, server->backup->servers[i].ip.data,
+                       sizeof(server->backup->servers[i].ip.data));
       return;
     }
   }
@@ -100,49 +111,83 @@ void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
                                         (i + 1));
   server->backup->servers[i].server = backup_server;
   server->backup->servers[i].local = local;
+  server->backup->servers[i].port = htons(port);
+  memset(server->backup->servers[i].ip.data, 0,
+        sizeof(server->backup->servers[i].ip.data));
+  silc_net_addr2bin(ip, server->backup->servers[i].ip.data,
+                   sizeof(server->backup->servers[i].ip.data));
   server->backup->servers_count++;
 }
 
-/* Returns the first backup router context. Returns NULL if we do not have
-   any backup servers. */
+/* Returns backup router for IP and port in `replacing' or NULL if there
+   does not exist backup router. */
 
-SilcServerEntry silc_server_backup_get(SilcServer server)
+SilcServerEntry silc_server_backup_get(SilcServer server, 
+                                      SilcServerID *server_id)
 {
-  SilcServerEntry backup_router;
   int i;
 
   if (!server->backup)
     return NULL;
 
   for (i = 0; i < server->backup->servers_count; i++) {
-    if (server->backup->servers[i].server) {
-      backup_router = server->backup->servers[i].server;
-      server->backup->servers[i].server = NULL;
-      return backup_router;
+    if (server->backup->servers[i].server &&
+       server->backup->servers[i].port == server_id->port &&
+       !memcmp(server->backup->servers[i].ip.data, server_id->ip.data,
+               sizeof(server_id->ip.data))) {
+      SILC_LOG_DEBUG(("Found backup router %s for %s",
+                     server->backup->servers[i].server->server_name,
+                     silc_id_render(server_id, SILC_ID_SERVER)));
+      return server->backup->servers[i].server;
     }
   }
 
   return NULL;
 }
 
-/* Deletes the backup server `server_entry. */
+/* Deletes the backup server `server_entry'. */
 
-void silc_server_backup_del(SilcServer server, 
-                           SilcServerEntry server_entry)
+void silc_server_backup_del(SilcServer server, SilcServerEntry server_entry)
 {
   int i;
 
   if (!server->backup)
-    return ;
+    return;
 
   for (i = 0; i < server->backup->servers_count; i++) {
     if (server->backup->servers[i].server == server_entry) {
+      SILC_LOG_DEBUG(("Removing %s as backup router",
+                     silc_id_render(server->backup->servers[i].server->id,
+                                    SILC_ID_SERVER)));
       server->backup->servers[i].server = NULL;
-      return;
+      memset(server->backup->servers[i].ip.data, 0,
+            sizeof(server->backup->servers[i].ip.data));
     }
   }
 }
 
+/* Frees all data allocated for backup routers.  Call this after deleting
+   all backup routers and when new routers are added no more, for example
+   when shutting down the server. */
+
+void silc_server_backup_free(SilcServer server)
+{
+  int i;
+
+  if (!server->backup)
+    return;
+
+  /* Delete existing servers if caller didn't do it */
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (server->backup->servers[i].server)
+      silc_server_backup_del(server, server->backup->servers[i].server);
+  }
+
+  silc_free(server->backup->servers);
+  silc_free(server->backup);
+  server->backup = NULL;
+}
+
 /* Marks the IP address and port from the `server_id' as  being replaced
    by backup router indicated by the `server'. If the router connects at
    a later time we can check whether it has been replaced by an backup
@@ -155,18 +200,19 @@ void silc_server_backup_replaced_add(SilcServer server,
   int i;
   SilcServerBackupReplaced *r = silc_calloc(1, sizeof(*r));;
 
-  if (!server->backup) {
+  if (!server->backup)
     server->backup = silc_calloc(1, sizeof(*server->backup));
-    server->backup->servers_count++;
-  }
   if (!server->backup->replaced) {
     server->backup->replaced = 
       silc_calloc(1, sizeof(*server->backup->replaced));
     server->backup->replaced_count = 1;
   }
 
+  SILC_LOG_DEBUG(("Replacing router %s with %s",
+                 silc_id_render(server_id, SILC_ID_SERVER),
+                 server_entry->server_name));
+
   memcpy(&r->ip, &server_id->ip, sizeof(server_id->ip));
-  //r->port = server_id->port;
   r->server = server_entry;
 
   for (i = 0; i < server->backup->replaced_count; i++) {
@@ -198,36 +244,39 @@ bool silc_server_backup_replaced_get(SilcServer server,
   if (!server->backup || !server->backup->replaced)
     return FALSE;
 
-  for (i = 0; i < server->backup->servers_count; i++) {
+  for (i = 0; i < server->backup->replaced_count; i++) {
     if (!server->backup->replaced[i])
       continue;
-    if (!memcmp(&server->backup->replaced[i]->ip, &server_id->ip,
-               sizeof(server_id->ip))) {
+    if (!memcmp(server->backup->replaced[i]->ip.data, server_id->ip.data,
+               sizeof(server_id->ip.data))) {
       if (server_entry)
        *server_entry = server->backup->replaced[i]->server;
+      SILC_LOG_DEBUG(("Router %s is replaced by %s",
+                     silc_id_render(server_id, SILC_ID_SERVER),
+                     server->backup->replaced[i]->server->server_name));
       return TRUE;
     }
   }
 
+  SILC_LOG_DEBUG(("Router %s is not replaced by backup router",
+                 silc_id_render(server_id, SILC_ID_SERVER)));
   return FALSE;
 }
 
-/* Deletes the IP address and port from the `server_id' from being replaced
-   by an backup router. */
+/* Deletes a replaced host by the set `server_entry. */
 
 void silc_server_backup_replaced_del(SilcServer server,
-                                    SilcServerID *server_id)
+                                    SilcServerEntry server_entry)
 {
   int i;
 
   if (!server->backup || !server->backup->replaced)
     return;
 
-  for (i = 0; i < server->backup->servers_count; i++) {
+  for (i = 0; i < server->backup->replaced_count; i++) {
     if (!server->backup->replaced[i])
       continue;
-    if (!memcmp(&server->backup->replaced[i]->ip, &server_id->ip,
-               sizeof(server_id->ip))) {
+    if (server->backup->replaced[i]->server == server_entry) {
       silc_free(server->backup->replaced[i]);
       server->backup->replaced[i] = NULL;
       return;
@@ -247,6 +296,7 @@ void silc_server_backup_broadcast(SilcServer server,
   SilcServerEntry backup;
   SilcSocketConnection sock;
   SilcBuffer buffer;
+  const SilcBufferStruct p;
   SilcIDListData idata;
   int i;
 
@@ -264,20 +314,31 @@ void silc_server_backup_broadcast(SilcServer server,
     if (!backup || backup->connection == sender || 
        server->backup->servers[i].local == FALSE)
       continue;
+    if (server->backup->servers[i].server == server->id_entry)
+      continue;
 
     idata = (SilcIDListData)backup;
     sock = backup->connection;
 
-    silc_packet_send_prepare(sock, 0, 0, buffer->len); 
-    silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
-    silc_packet_encrypt(idata->send_key, idata->hmac_send, 
-                       sock->outbuf, sock->outbuf->len);
+    if (!silc_packet_send_prepare(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);
+    silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++, 
+                       (SilcBuffer)&p, p.len);
 
-    SILC_LOG_HEXDUMP(("Broadcasted packet, len %d", sock->outbuf->len),
-                    sock->outbuf->data, sock->outbuf->len);
+    SILC_LOG_HEXDUMP(("Broadcasted packet, len %d", p.len), p.data, p.len);
 
     /* Now actually send the packet */
     silc_server_packet_send_real(server, sock, FALSE);
+
+    /* Check for mandatory rekey */
+    if (idata->psn_send == SILC_SERVER_REKEY_THRESHOLD)
+      silc_schedule_task_add(server->schedule, sender->sock,
+                            silc_server_rekey_callback, sender, 0, 1,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
   }
 }
 
@@ -296,7 +357,7 @@ void silc_server_backup_send(SilcServer server,
                             SilcPacketType type,
                             SilcPacketFlags flags,
                             unsigned char *data,
-                            uint32 data_len,
+                            SilcUInt32 data_len,
                             bool force_send,
                             bool local)
 {
@@ -307,20 +368,20 @@ void silc_server_backup_send(SilcServer server,
   if (!server->backup || server->server_type != SILC_ROUTER)
     return;
 
-  SILC_LOG_DEBUG(("Start"));
-
   for (i = 0; i < server->backup->servers_count; i++) {
     backup = server->backup->servers[i].server;
-    if (!backup)
-      continue;
-
-    if (sender == backup)
+    if (!backup || sender == backup)
       continue;
-
     if (local && server->backup->servers[i].local == FALSE)
       continue;
+    if (server->backup->servers[i].server == server->id_entry)
+      continue;
 
     sock = backup->connection;
+
+    SILC_LOG_DEBUG(("Sending %s packet to backup router %s (%s)",
+                   silc_get_packet_name(type), sock->hostname, sock->ip));
+
     silc_server_packet_send(server, backup->connection, type, flags, 
                            data, data_len, force_send);
   }
@@ -338,7 +399,7 @@ void silc_server_backup_send_dest(SilcServer server,
                                  void *dst_id,
                                  SilcIdType dst_id_type,
                                  unsigned char *data,
-                                 uint32 data_len,
+                                 SilcUInt32 data_len,
                                  bool force_send,
                                  bool local)
 {
@@ -349,26 +410,37 @@ void silc_server_backup_send_dest(SilcServer server,
   if (!server->backup || server->server_type != SILC_ROUTER)
     return;
 
-  SILC_LOG_DEBUG(("Start"));
-
   for (i = 0; i < server->backup->servers_count; i++) {
     backup = server->backup->servers[i].server;
-    if (!backup)
+    if (!backup || sender == backup)
       continue;
-
-    if (sender == backup)
-      continue;
-
     if (local && server->backup->servers[i].local == FALSE)
       continue;
+    if (server->backup->servers[i].server == server->id_entry)
+      continue;
 
     sock = backup->connection;
+
+    SILC_LOG_DEBUG(("Sending %s packet to backup router %s (%s)",
+                   silc_get_packet_name(type), sock->hostname, sock->ip));
+
     silc_server_packet_send_dest(server, backup->connection, type, flags, 
                                 dst_id, dst_id_type, data, data_len, 
                                 force_send);
   }
 }
 
+SILC_TASK_CALLBACK(silc_server_backup_timeout)
+{
+  SilcProtocol protocol = context;
+  SilcServer server = app_context;
+
+  SILC_LOG_INFO(("Timeout occurred during backup resuming protocol"));
+  silc_protocol_cancel(protocol, server->schedule);
+  protocol->state = SILC_PROTOCOL_STATE_ERROR;
+  silc_protocol_execute_final(protocol, server->schedule);
+}
+
 /* Processes incoming RESUME_ROUTER packet. This can give the packet
    for processing to the protocol handler or allocate new protocol if
    start command is received. */
@@ -377,40 +449,70 @@ void silc_server_backup_resume_router(SilcServer server,
                                      SilcSocketConnection sock, 
                                      SilcPacketContext *packet)
 {
-  uint8 type, session;
-  int ret;
+  SilcUInt8 type, session;
+  SilcServerBackupProtocolContext ctx;
+  int i, ret;
 
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      sock->type == SILC_SOCKET_TYPE_UNKNOWN)
+      sock->type == SILC_SOCKET_TYPE_UNKNOWN) {
+    SILC_LOG_DEBUG(("Bad packet received"));
     return;
-
-  SILC_LOG_DEBUG(("Start"));
+  }
 
   ret = silc_buffer_unformat(packet->buffer,
                             SILC_STR_UI_CHAR(&type),
                             SILC_STR_UI_CHAR(&session),
                             SILC_STR_END);
-  if (ret < 0)
+  if (ret < 0) {
+    SILC_LOG_ERROR(("Malformed resume router packet received"));
     return;
+  }
   
+  /* Activate the protocol for this socket if necessary */
+  if ((type == SILC_SERVER_BACKUP_RESUMED || 
+      type == SILC_SERVER_BACKUP_RESUMED_GLOBAL) &&
+      sock->type == SILC_SOCKET_TYPE_ROUTER && !sock->protocol && 
+      ((SilcIDListData)sock->user_data)->status & 
+      SILC_IDLIST_STATUS_DISABLED) {
+    SilcServerEntry backup_router;
+
+    if (silc_server_backup_replaced_get(server, 
+                                       ((SilcServerEntry)sock->
+                                        user_data)->id, 
+                                       &backup_router)) {
+      SilcSocketConnection bsock = 
+       (SilcSocketConnection)backup_router->connection;
+      if (bsock->protocol && bsock->protocol->protocol &&
+         bsock->protocol->protocol->type == SILC_PROTOCOL_SERVER_BACKUP) {
+       sock->protocol = bsock->protocol;
+       ctx = sock->protocol->context;
+       ctx->sock = sock;
+      }
+    }
+  }
+
   /* If the backup resuming protocol is active then process the packet
      in the protocol. */
   if (sock->protocol && sock->protocol->protocol &&
       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_BACKUP) {
-    SilcServerBackupProtocolContext ctx = sock->protocol->context;
-    int i;
-
+    ctx = sock->protocol->context;
     ctx->type = type;
 
-    for (i = 0; i < ctx->sessions_count; i++) {
-      if (session == ctx->sessions[i].session) {
-       ctx->session = session;
-       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
-       return;
+    if (type != SILC_SERVER_BACKUP_RESUMED &&
+       type != SILC_SERVER_BACKUP_RESUMED_GLOBAL) {
+      for (i = 0; i < ctx->sessions_count; i++) {
+       if (session == ctx->sessions[i].session) {
+         ctx->session = session;
+         silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+         return;
+       }
       }
+    } else {
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      return;
     }
 
-    SILC_LOG_DEBUG(("Bad resume router packet"));
+    SILC_LOG_ERROR(("Bad resume router packet RESUMED %d", type));
     return;
   }
 
@@ -420,14 +522,14 @@ void silc_server_backup_resume_router(SilcServer server,
      immediately after we've connected to our primary router. */
 
   if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
-      server->router == sock->user_data &&
+      sock && SILC_PRIMARY_ROUTE(server) == sock &&
       type == SILC_SERVER_BACKUP_REPLACED) {
     /* We have been replaced by an backup router in our cell. We must
        mark our primary router connection disabled since we are not allowed
        to use it at this moment. */
     SilcIDListData idata = (SilcIDListData)sock->user_data;
-
-    SILC_LOG_DEBUG(("We are replaced by an backup router in this cell"));
+    SILC_LOG_INFO(("We are replaced by an backup router in this cell, will "
+                  "wait until backup resuming protocol is executed"));
     idata->status |= SILC_IDLIST_STATUS_DISABLED;
     return;
   }
@@ -446,12 +548,17 @@ void silc_server_backup_resume_router(SilcServer server,
     proto_ctx->start = time(0);
 
     SILC_LOG_DEBUG(("Starting backup resuming protocol as responder"));
+    SILC_LOG_INFO(("Starting backup resuming protocol"));
 
     /* Run the backup resuming protocol */
     silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
                        &sock->protocol, proto_ctx, 
                        silc_server_protocol_backup_done);
     silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_backup_timeout,
+                          sock->protocol, 30, 0, SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_NORMAL);
   }
 }
 
@@ -459,21 +566,23 @@ void silc_server_backup_resume_router(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_backup_connect_to_router)
 {
+  SilcServer server = app_context;
   SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
   int sock;
+  const char *server_ip;
 
   SILC_LOG_DEBUG(("Connecting to router %s:%d", sconn->remote_host,
                  sconn->remote_port));
 
   /* Connect to remote host */
-  sock = silc_net_create_connection(server->config->listen_port->local_ip,
-                                   sconn->remote_port,
+  server_ip = server->config->server_info->primary == NULL ? NULL :
+    server->config->server_info->primary->server_ip;
+  sock = silc_net_create_connection(server_ip, sconn->remote_port,
                                    sconn->remote_host);
   if (sock < 0) {
     silc_schedule_task_add(server->schedule, 0,
                           silc_server_backup_connect_to_router,
-                          context, 0, 2, SILC_TASK_TIMEOUT, 
+                          context, 5, 0, SILC_TASK_TIMEOUT, 
                           SILC_TASK_PRI_NORMAL);
     return;
   }
@@ -487,24 +596,29 @@ SILC_TASK_CALLBACK(silc_server_backup_connect_to_router)
    connection is created. */
 
 void silc_server_backup_reconnect(SilcServer server,
-                                 const char *ip, uint16 port,
+                                 const char *ip, SilcUInt16 port,
                                  SilcServerConnectRouterCallback callback,
                                  void *context)
 {
   SilcServerConnection sconn;
 
+  SILC_LOG_INFO(("Attempting to reconnect to primary router"));
+
   sconn = silc_calloc(1, sizeof(*sconn));
-  sconn->server = server;
   sconn->remote_host = strdup(ip);
   sconn->remote_port = port;
   sconn->callback = callback;
   sconn->callback_context = context;
+  sconn->no_reconnect = TRUE;
   silc_schedule_task_add(server->schedule, 0, 
                         silc_server_backup_connect_to_router,
-                        sconn, 2, 0, SILC_TASK_TIMEOUT,
+                        sconn, 1, 0, SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
 }
 
+/* Task that is called after backup router has connected back to
+   primary router and we are starting the resuming protocol */
+
 SILC_TASK_CALLBACK(silc_server_backup_connected_later)
 {
   SilcServerBackupProtocolContext proto_ctx = 
@@ -513,12 +627,18 @@ SILC_TASK_CALLBACK(silc_server_backup_connected_later)
   SilcSocketConnection sock = proto_ctx->sock;
 
   SILC_LOG_DEBUG(("Starting backup resuming protocol as initiator"));
+  SILC_LOG_INFO(("Starting backup resuming protocol"));
 
   /* Run the backup resuming protocol */
   silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
                      &sock->protocol, proto_ctx, 
                      silc_server_protocol_backup_done);
   silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+
+  silc_schedule_task_add(server->schedule, sock->sock,
+                        silc_server_backup_timeout,
+                        sock->protocol, 30, 0, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 }
 
 /* Called when we've established connection back to our primary router
@@ -530,8 +650,21 @@ void silc_server_backup_connected(SilcServer server,
                                  void *context)
 {
   SilcServerBackupProtocolContext proto_ctx;
-  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
+  SilcSocketConnection sock;
+
+  if (!server_entry) {
+    /* Try again */
+    SilcServerConfigRouter *primary;
+    primary = silc_server_config_get_primary_router(server);
+    if (primary)
+      silc_server_backup_reconnect(server,
+                                  primary->host, primary->port,
+                                  silc_server_backup_connected,
+                                  context);
+    return;
+  }
 
+  sock = (SilcSocketConnection)server_entry->connection;
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = server;
   proto_ctx->sock = sock;
@@ -557,12 +690,31 @@ static void silc_server_backup_connect_primary(SilcServer server,
                                               void *context)
 {
   SilcSocketConnection backup_router = (SilcSocketConnection)context;
-  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
-  SilcIDListData idata = (SilcIDListData)server_entry;
-  SilcServerBackupProtocolContext ctx = 
-    (SilcServerBackupProtocolContext)backup_router->protocol->context;
+  SilcServerBackupProtocolContext ctx;
+  SilcSocketConnection sock;
+  SilcIDListData idata;
   SilcBuffer buffer;
 
+  if (!server_entry) {
+    /* Try again */
+    SilcServerConfigRouter *primary;
+    primary = silc_server_config_get_primary_router(server);
+    if (primary)
+      silc_server_backup_reconnect(server,
+                                  primary->host, primary->port,
+                                  silc_server_backup_connect_primary,
+                                  context);
+    return;
+  }
+
+  ctx = (SilcServerBackupProtocolContext)backup_router->protocol->context;
+  sock = (SilcSocketConnection)server_entry->connection;
+  idata = (SilcIDListData)server_entry;
+
+  SILC_LOG_DEBUG(("Sending CONNECTED packet (session %d)", ctx->session));
+  SILC_LOG_INFO(("Sending CONNECTED (session %d) to backup router",
+               ctx->session));
+
   /* Send the CONNECTED packet back to the backup router. */
   buffer = silc_buffer_alloc(2);
   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
@@ -575,6 +727,10 @@ static void silc_server_backup_connect_primary(SilcServer server,
                          buffer->data, buffer->len, FALSE);
   silc_buffer_free(buffer);
 
+  /* The primary connection is disabled until it sends the RESUMED packet
+     to us. */
+  idata->status |= SILC_IDLIST_STATUS_DISABLED;
+
   /* Move this protocol context from this backup router connection to
      the primary router connection since it will send the subsequent
      packets in this protocol. We don't talk with backup router 
@@ -582,72 +738,42 @@ static void silc_server_backup_connect_primary(SilcServer server,
   sock->protocol = backup_router->protocol;
   ctx->sock = (SilcSocketConnection)server_entry->connection;
   backup_router->protocol = NULL;
-
-  /* The primary connection is disabled until it sends the RESUMED packet
-     to us. */
-  idata->status |= SILC_IDLIST_STATUS_DISABLED;
 }
 
-/* Resume protocol with RESUME_ROUTER packet: 
-
-   SILC_PACKET_RESUME_ROUTER:
-
-   <uint8 type> <uint8 Session ID>
-
-   <type>          = the protocol opcode
-   <Session ID>    = Identifier for this packet and any subsequent reply
-                     packets must include this identifier.
-
-   Types:
-
-     1    = To router: Comensing backup resuming protocol. This will
-            indicate that the sender is backup router acting as primary
-            and the receiver is primary router that has been replaced by
-           the backup router.
-
-           To server. Comensing backup resuming protocol. This will
-           indicate that the sender is backup router and the receiver
-           must reconnect to the real primary router of the cell.
-
-     2    = To Router: Comesning backup resuming protocol in another
-            cell.  The receiver will connect to its primary router 
-           (the router that is now online again) but will not use
-           the link.  If the receiver is not configured to connect
-           to any router it does as locally configured.  The sender
-           is always backup router.
-
-           To server: this is never sent to server.
-
-     3    = To backup router: Sender is normal server or router and it
-            tells to backup router that they have connected to the
-           primary router.  Backup router never sends this type.
-
-     4    = To router: Ending backup resuming protocol. This is sent
-            to the real primary router to tell that it can take over
-           the task as being primary router.
-
-           To server: same as sending for router.
-
-           Backup router sends this also to the primary route but only
-           after it has sent them to normal servers and has purged all
-           traffic coming from normal servers.
-
-     5    = To router: Sender is the real primary router after it has
-            received type 4 from backup router. To tell that it is again
-           primary router of the cell.
-
-     20   = To router: This is sent only when router is connecting to
-            another router and has been replaced by an backup router.
-           The sender knows that the connectee has been replaced.
+SILC_TASK_CALLBACK(silc_server_backup_send_resumed)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServer server = ctx->server;
+  SilcBuffer packet;
+  int i;
 
- */
+  for (i = 0; i < ctx->sessions_count; i++)
+    if (ctx->sessions[i].server_entry == ctx->sock->user_data)
+      ctx->session = ctx->sessions[i].session;
+  
+  /* We've received all the CONNECTED packets and now we'll send the
+     ENDING packet to the new primary router. */
+  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_ENDING),
+                    SILC_STR_UI_CHAR(ctx->session),
+                    SILC_STR_END);
+  silc_server_packet_send(server, ctx->sock, 
+                         SILC_PACKET_RESUME_ROUTER, 0, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+  
+  protocol->state = SILC_PROTOCOL_STATE_END;
+}
 
 /* Backup resuming protocol. This protocol is executed when the primary
    router wants to resume its position as being primary router. */
 
 SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
 {
-  SilcProtocol protocol = (SilcProtocol)protocol;
+  SilcProtocol protocol = (SilcProtocol)context;
   SilcServerBackupProtocolContext ctx = protocol->context;
   SilcServer server = ctx->server;
   SilcBuffer packet;
@@ -656,13 +782,9 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
   SilcServerEntry server_entry;
   int i;
 
-  SILC_LOG_DEBUG(("Start"));
-
   if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
     protocol->state = SILC_PROTOCOL_STATE_START;
 
-  SILC_LOG_DEBUG(("State=%d", protocol->state));
-
   switch(protocol->state) {
   case SILC_PROTOCOL_STATE_START:
     if (ctx->responder == FALSE) {
@@ -676,8 +798,9 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
        if (silc_idcache_list_first(list, &id_cache)) {
          while (id_cache) {
            server_entry = (SilcServerEntry)id_cache->context;
-           if ((server_entry == server->id_entry) || 
-               !server_entry->connection) {
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key ||
+               (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)) {
              if (!silc_idcache_list_next(list, &id_cache))
                break;
              else
@@ -691,6 +814,15 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
            ctx->sessions[ctx->sessions_count].connected = FALSE;
            ctx->sessions[ctx->sessions_count].server_entry = server_entry;
 
+           SILC_LOG_DEBUG(("Sending START to %s (session %d)",
+                           server_entry->server_name, ctx->sessions_count));
+           SILC_LOG_INFO(("Expecting CONNECTED from %s (session %d)",
+                           server_entry->server_name, ctx->sessions_count));
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
            if (server_entry->server_type == SILC_ROUTER)
              packet->data[0] = SILC_SERVER_BACKUP_START;
            else
@@ -709,37 +841,127 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
        silc_idcache_list_free(list);
       }
 
-      /* Now, announce all of our information to the primary router */
-      if (server->server_type == SILC_ROUTER)
-       silc_server_announce_servers(server, FALSE, 0);
-      silc_server_announce_clients(server, 0);
-      silc_server_announce_channels(server, 0);
+      if (silc_idcache_get_all(server->global_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key ||
+               (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           ctx->sessions = silc_realloc(ctx->sessions,
+                                        sizeof(*ctx->sessions) *
+                                        (ctx->sessions_count + 1));
+           ctx->sessions[ctx->sessions_count].session = ctx->sessions_count;
+           ctx->sessions[ctx->sessions_count].connected = FALSE;
+           ctx->sessions[ctx->sessions_count].server_entry = server_entry;
+
+           SILC_LOG_DEBUG(("Sending START to %s (session %d)", 
+                           server_entry->server_name, ctx->sessions_count));
+           SILC_LOG_INFO(("Expecting CONNECTED from %s (session %d)",
+                           server_entry->server_name, ctx->sessions_count));
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_START;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_START_GLOBAL;
+           packet->data[1] = ctx->sessions_count;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+           ctx->sessions_count++;
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
 
       silc_buffer_free(packet);
 
+      /* If we are not standalone and our primary is not the one we've
+        talking to now, then announce our information to it since we
+        haven't done that yet.  Standalone backup router announces
+        these during connecting to the primary. */
+      if (!server->standalone && SILC_PRIMARY_ROUTE(server) != ctx->sock) {
+       silc_server_announce_servers(server, TRUE, 0, ctx->sock);
+       silc_server_announce_clients(server, 0, ctx->sock);
+       silc_server_announce_channels(server, 0, ctx->sock);
+      }
+
       protocol->state++;
     } else {
       /* Responder of the protocol. */
-      SilcServerConfigSectionServerConnection *primary;
+      SilcServerConfigRouter *primary;
 
       /* We should have received START or START_GLOBAL packet */
       if (ctx->type != SILC_SERVER_BACKUP_START &&
          ctx->type != SILC_SERVER_BACKUP_START_GLOBAL) {
-       SILC_LOG_DEBUG(("Bad resume router packet"));
+       SILC_LOG_ERROR(("Bad resume router packet START %d", ctx->type));
        break;
       }
 
       /* Connect to the primary router that was down that is now supposed
         to be back online. We send the CONNECTED packet after we've
         established the connection to the primary router. */
-      primary = silc_server_config_get_primary_router(server->config);
-      if (primary)
+      primary = silc_server_config_get_primary_router(server);
+      if (primary && server->backup_primary &&
+         !silc_server_num_sockets_by_remote(server,
+                                            silc_net_is_ip(primary->host) ?
+                                            primary->host : NULL,
+                                            silc_net_is_ip(primary->host) ?
+                                            NULL : primary->host,
+                                            primary->port,
+                                            SILC_SOCKET_TYPE_ROUTER)) {
+       SILC_LOG_DEBUG(("Received START (session %d), reconnect to router",
+                       ctx->session));
        silc_server_backup_reconnect(server,
                                     primary->host, primary->port,
                                     silc_server_backup_connect_primary,
                                     ctx->sock);
+      } else {
+       /* Nowhere to connect just return the CONNECTED packet */
+       SILC_LOG_DEBUG(("Received START (session %d), send CONNECTED back",
+                       ctx->session));
+       SILC_LOG_INFO(("Sending CONNECTED (session %d) to backup router",
+                     ctx->session));
+
+       /* Send the CONNECTED packet back to the backup router. */
+       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_CONNECTED),
+                          SILC_STR_UI_CHAR(ctx->session),
+                          SILC_STR_END);
+       silc_server_packet_send(server, ctx->sock, 
+                               SILC_PACKET_RESUME_ROUTER, 0, 
+                               packet->data, packet->len, FALSE);
+       silc_buffer_free(packet);
+      }
 
-      protocol->state++;
+      if (server->server_type == SILC_ROUTER &&
+         (!server->router || 
+          server->router->data.status & SILC_IDLIST_STATUS_DISABLED))
+       protocol->state++;
+      else
+       protocol->state = SILC_PROTOCOL_STATE_END;
+
+      ctx->sessions = silc_realloc(ctx->sessions,
+                                  sizeof(*ctx->sessions) *
+                                  (ctx->sessions_count + 1));
+      ctx->sessions[ctx->sessions_count].session = ctx->session;
+      ctx->sessions_count++;
     }
     break;
 
@@ -749,55 +971,71 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
 
       /* We should have received CONNECTED packet */
       if (ctx->type != SILC_SERVER_BACKUP_CONNECTED) {
-       SILC_LOG_DEBUG(("Bad resume router packet"));
+       SILC_LOG_ERROR(("Bad resume router packet CONNECTED %d", ctx->type));
        break;
       }
 
       for (i = 0; i < ctx->sessions_count; i++) {
        if (ctx->sessions[i].session == ctx->session) {
          ctx->sessions[i].connected = TRUE;
+         SILC_LOG_INFO(("Received CONNECTED from %s (session %d)",
+                        ctx->sessions[i].server_entry->server_name,
+                        ctx->session));
+         SILC_LOG_DEBUG(("Received CONNECTED (session %d)", ctx->session));
+         break;
        }
       }
 
       for (i = 0; i < ctx->sessions_count; i++) {
        if (!ctx->sessions[i].connected)
-         break;
+         return;
       }
 
-      /* We've received all the CONNECTED packets and now we'll send the
-        ENDING packet to the new primary router. */
-      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_ENDING),
-                        SILC_STR_UI_CHAR(ctx->session),
-                        SILC_STR_END);
-      silc_server_packet_send(server, ctx->sock, 
-                             SILC_PACKET_RESUME_ROUTER, 0, 
-                             packet->data, packet->len, FALSE);
-      silc_buffer_free(packet);
+      SILC_LOG_INFO(("All sessions have returned CONNECTED packets, "
+                    "continuing"));
+      SILC_LOG_DEBUG(("Sending ENDING packet to primary router"));
 
-      protocol->state = SILC_PROTOCOL_STATE_END;
+      /* Send with a timeout */
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_backup_send_resumed,
+                            protocol, 1, 0, SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+      return;
     } else {
       /* Responder */
 
       /* We should have been received ENDING packet */
       if (ctx->type != SILC_SERVER_BACKUP_ENDING) {
-       SILC_LOG_DEBUG(("Bad resume router packet"));
+       SILC_LOG_ERROR(("Bad resume router packet ENDING %d", ctx->type));
        break;
       }
 
+      SILC_LOG_DEBUG(("Received ENDING packet, we are going to resume now"));
+
       /* This state is received by the primary router but also servers
         and perhaps other routers so check that if we are the primary
         router of the cell then start sending RESUMED packets.  If we
         are normal server or one of those other routers then procede
         to next state. */
-      if (!(server->router->data.status & SILC_IDLIST_STATUS_DISABLED)) {
+      if (server->router &&
+         !(server->router->data.status & SILC_IDLIST_STATUS_DISABLED) &&
+         silc_server_config_is_primary_route(server)) {
        /* We'll wait for RESUMED packet */
        protocol->state = SILC_PROTOCOL_STATE_END;
        break;
       }
 
+      /* Switch announced informations to our primary router of using the
+        backup router. */
+      silc_server_local_servers_toggle_enabled(server, TRUE);
+      silc_server_update_servers_by_server(server, ctx->sock->user_data, 
+                                          server->router);
+      silc_server_update_clients_by_server(server, ctx->sock->user_data,
+                                          server->router, TRUE);
+      if (server->server_type == SILC_SERVER)
+       silc_server_update_channels_by_server(server, ctx->sock->user_data, 
+                                             server->router);
+
       packet = silc_buffer_alloc(2);
       silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
 
@@ -806,19 +1044,64 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
        if (silc_idcache_list_first(list, &id_cache)) {
          while (id_cache) {
            server_entry = (SilcServerEntry)id_cache->context;
-           if ((server_entry == server->id_entry) || 
-               !server_entry->connection) {
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key) {
              if (!silc_idcache_list_next(list, &id_cache))
                break;
              else
                continue;
            }
 
+           SILC_LOG_DEBUG(("Sending RESUMED to %s",
+                           server_entry->server_name));
+
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED_GLOBAL;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
+
+      if (silc_idcache_get_all(server->global_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           SILC_LOG_DEBUG(("Sending RESUMED to %s",
+                           server_entry->server_name));
+
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
            if (server_entry->server_type == SILC_ROUTER)
              packet->data[0] = SILC_SERVER_BACKUP_RESUMED;
            else
              packet->data[0] = SILC_SERVER_BACKUP_RESUMED_GLOBAL;
-           packet->data[1] = ctx->sessions_count;
            silc_server_packet_send(server, server_entry->connection,
                                    SILC_PACKET_RESUME_ROUTER, 0, 
                                    packet->data, packet->len, FALSE);
@@ -833,6 +1116,9 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
 
       silc_buffer_free(packet);
 
+      SILC_LOG_INFO(("We are now the primary router of our cell again"));
+      server->wait_backup = FALSE;
+
       /* For us this is the end of this protocol. */
       if (protocol->final_callback)
        silc_protocol_execute_final(protocol, server->schedule);
@@ -843,46 +1129,68 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
 
   case SILC_PROTOCOL_STATE_END:
     {
-      SilcIDListData idata;
-      SilcServerEntry primary;
-      SilcServerEntry backup_router;
+      SilcServerEntry router, backup_router;
 
       /* We should have been received RESUMED packet from our primary
         router. */
       if (ctx->type != SILC_SERVER_BACKUP_RESUMED &&
          ctx->type != SILC_SERVER_BACKUP_RESUMED_GLOBAL) {
-       SILC_LOG_DEBUG(("Bad resume router packet"));
+       SILC_LOG_ERROR(("Bad resume router packet RESUMED %d", ctx->type));
        break;
       }
 
-      /* We have now new primary router. All traffic goes there from now on. */
+      SILC_LOG_INFO(("Received RESUMED from new primary router"));
+
       if (server->backup_router)
        server->server_type = SILC_BACKUP_ROUTER;
 
-      primary = (SilcServerEntry)ctx->sock->user_data;
-      if (silc_server_backup_replaced_get(server, primary->id, 
+      /* We have now new primary router. All traffic goes there from now on. */
+      router = (SilcServerEntry)ctx->sock->user_data;
+      if (silc_server_backup_replaced_get(server, router->id, 
                                          &backup_router)) {
 
        if (backup_router == server->router) {
-         server->id_entry->router = ctx->sock->user_data;
-         server->router = ctx->sock->user_data;
-         SILC_LOG_DEBUG(("Switching back to primary router %s",
-                       server->router->server_name));
-         idata = (SilcIDListData)server->router;
-         idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
+         /* We have new primary router now */
+         server->id_entry->router = router;
+         server->router = router;
+         SILC_LOG_INFO(("Switching back to primary router %s",
+                        server->router->server_name));
+       } else {
+         /* We are connected to new primary and now continue using it */
+         SILC_LOG_INFO(("Resuming the use of primary router %s",
+                        router->server_name));
        }
-
-       /* Announce all of our information to the new primary router. We
-          announce all that was updated after the protocol was started since
-          the router knows all the older stuff. */
+       server->backup_primary = FALSE;
+
+       /* Update the client entries of the backup router to the new 
+          router */
+       silc_server_local_servers_toggle_enabled(server, FALSE);
+       router->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+       silc_server_update_servers_by_server(server, backup_router, router);
+       silc_server_update_clients_by_server(server, NULL, router, FALSE);
+       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 - 60);
-       
+         silc_server_announce_servers(server, FALSE, ctx->start,
+                                      router->connection);
+
        /* Announce our clients and channels to the router */
-       silc_server_announce_clients(server, ctx->start - 60);
-       silc_server_announce_channels(server, ctx->start - 60);
+       silc_server_announce_clients(server, ctx->start,
+                                    router->connection);
+       silc_server_announce_channels(server, ctx->start,
+                                     router->connection);
       }
 
+      /* Send notify about primary router going down to local operators */
+      SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+                            SILC_NOTIFY_TYPE_NONE,
+                            ("%s resumed the use of primary router %s",
+                             server->server_name,
+                             server->router->server_name));
+
       /* Protocol has ended, call the final callback */
       if (protocol->final_callback)
        silc_protocol_execute_final(protocol, server->schedule);
@@ -892,9 +1200,20 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
     break;
 
   case SILC_PROTOCOL_STATE_ERROR:
+    /* Protocol has ended, call the final callback */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
     break;
 
   case SILC_PROTOCOL_STATE_FAILURE:
+    /* Protocol has ended, call the final callback */
+    SILC_LOG_ERROR(("Error during backup resume: received Failure"));
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
     break;
 
   case SILC_PROTOCOL_STATE_UNKNOWN:
@@ -904,5 +1223,118 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
 
 SILC_TASK_CALLBACK(silc_server_protocol_backup_done)
 {
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServer server = ctx->server;
+  SilcServerEntry server_entry;
+  SilcSocketConnection sock;
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+
+  silc_schedule_task_del_by_context(server->schedule, protocol);
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    SILC_LOG_ERROR(("Error occurred during backup router resuming protcool"));
+  }
+
+  if (server->server_shutdown)
+    return;
+
+  /* Remove this protocol from all server entries that has it */
+  if (silc_idcache_get_all(server->local_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       sock = (SilcSocketConnection)server_entry->connection;
+
+       if (sock->protocol == protocol) {
+         sock->protocol = NULL;
+
+         /* Backup closes connection and reconnects if error occurred */
+         if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router) {
+           if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+               protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+             server->backup_noswitch = TRUE;
+             server->server_type = SILC_BACKUP_ROUTER;
+
+             if (sock->user_data)
+               silc_server_free_sock_user_data(server, sock, NULL);
+             silc_server_close_connection(server, sock);
+
+             silc_schedule_task_add(server->schedule, 0,
+                                    silc_server_connect_to_router,
+                                    server, 1, 0,
+                                    SILC_TASK_TIMEOUT,
+                                    SILC_TASK_PRI_NORMAL);
+
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             continue;
+           }
+         }
+
+         if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+       }
+       
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (silc_idcache_get_all(server->global_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       sock = (SilcSocketConnection)server_entry->connection;
+
+       if (sock->protocol == protocol) {
+         sock->protocol = NULL;
+
+         /* Backup closes connection and reconnects if error occurred */
+         if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router) {
+           if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+               protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+             server->backup_noswitch = TRUE;
+             server->server_type = SILC_BACKUP_ROUTER;
+
+             if (sock->user_data)
+               silc_server_free_sock_user_data(server, sock, NULL);
+             silc_server_close_connection(server, sock);
+
+             silc_schedule_task_add(server->schedule, 0,
+                                    silc_server_connect_to_router,
+                                    server, 1, 0,
+                                    SILC_TASK_TIMEOUT,
+                                    SILC_TASK_PRI_NORMAL);
+
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             continue;
+           }
+         }
+
+         if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+       }
+       
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (protocol->state != SILC_PROTOCOL_STATE_ERROR &&
+      protocol->state != SILC_PROTOCOL_STATE_FAILURE)
+    SILC_LOG_INFO(("Backup resuming protocol ended successfully"));
 
+  if (ctx->sock->protocol)
+    ctx->sock->protocol = NULL;
+  silc_protocol_free(protocol);
+  silc_free(ctx->sessions);
+  silc_free(ctx);
 }