Fixed backup router shutdown crash
[silc.git] / apps / silcd / server_backup.c
index 4bcf0b115f09601b93a695aac91ffa397f0db79b..3bde1802a3f00cb405bba34c5fa26c89f50b42f4 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  server_backup.c 
+  server_backup.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 Pekka Riikonen
+  Copyright (C) 2001 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #include "server_internal.h"
 
 SILC_TASK_CALLBACK(silc_server_protocol_backup_done);
+SILC_TASK_CALLBACK(silc_server_backup_announce_watches);
+
+static void silc_server_backup_connect_primary(SilcServer server,
+                                              SilcServerEntry server_entry,
+                                              void *context);
+
+
+/************************** Types and Definitions ***************************/
 
 /* Backup router */
 typedef struct {
   SilcServerEntry server;
   SilcIDIP ip;
-  uint16 port;
-  bool local;
+  SilcUInt16 port;
+  SilcBool local;
 } SilcServerBackupEntry;
 
 /* Holds IP address and port of the primary router that was replaced
    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;
-  bool connected;
+  SilcUInt8 session;
+  SilcBool connected;
   SilcServerEntry server_entry;
 } SilcServerBackupProtocolSession;
 
 /* Backup resuming protocol context  */
 typedef struct {
   SilcServer server;
-  SilcSocketConnection sock;
-  bool responder;
-  uint8 type;
-  uint8 session;
+  SilcPacketStream sock;
+  SilcUInt8 type;
+  SilcUInt8 session;
   SilcServerBackupProtocolSession *sessions;
-  uint32 sessions_count;
+  SilcUInt32 sessions_count;
+  SilcUInt32 initiator_restart;
   long start;
+  int state;
+  unsigned int responder        : 1;
+  unsigned int received_failure : 1;
+  unsigned int timeout          : 1;
+  unsigned int error            : 1;
 } *SilcServerBackupProtocolContext;
 
+
+/********************* Backup Configuration Routines ************************/
+
 /* 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'
@@ -72,27 +88,38 @@ typedef struct {
    in the local cell, if FALSE it is in some other cell. */
 
 void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
-                           const char *ip, int port, bool local)
+                           const char *ip, int port, SilcBool local)
 {
   int i;
 
-  SILC_LOG_DEBUG(("Start"));
-
   if (!ip)
     return;
 
-  if (!server->backup)
+  if (!server->backup) {
     server->backup = silc_calloc(1, sizeof(*server->backup));
+    if (!server->backup)
+      return;
+  }
+
+  /* See if already added */
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (server->backup->servers[i].server == backup_server)
+      return;
+  }
+
+  SILC_LOG_DEBUG(("Backup router %s will replace %s",
+                 backup_server->data.sconn ?
+                 backup_server->data.sconn->remote_host : "(me)", ip));
 
   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 = SILC_SWAB_16(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[i].port = port;
       return;
     }
   }
@@ -103,84 +130,108 @@ 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 = SILC_SWAB_16(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[i].port = server_id->port;
   server->backup->servers_count++;
 }
 
-/* Returns backup router for IP and port in `replacing' or NULL if there
+/* Returns backup router for IP and port in `server_id' 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)
 {
   int i;
 
-  SILC_LOG_DEBUG(("Start"));
-
   if (!server->backup)
     return NULL;
 
   for (i = 0; i < server->backup->servers_count; i++) {
-    SILC_LOG_HEXDUMP(("IP"), server_id->ip.data, 16);
-    SILC_LOG_HEXDUMP(("IP"), server->backup->servers[i].ip.data, 16);
     if (server->backup->servers[i].server &&
-       !memcmp(&server->backup->servers[i].ip, &server_id->ip.data,
-               sizeof(server_id->ip.data)))
+       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'. */
+
 void silc_server_backup_del(SilcServer server, SilcServerEntry server_entry)
 {
   int i;
 
-  SILC_LOG_DEBUG(("Start"));
-
   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
    router. */
 
-void silc_server_backup_replaced_add(SilcServer server, 
+void silc_server_backup_replaced_add(SilcServer server,
                                     SilcServerID *server_id,
                                     SilcServerEntry server_entry)
 {
   int i;
   SilcServerBackupReplaced *r = silc_calloc(1, sizeof(*r));;
 
-  SILC_LOG_DEBUG(("Start"));
-
   if (!server->backup)
     server->backup = silc_calloc(1, sizeof(*server->backup));
   if (!server->backup->replaced) {
-    server->backup->replaced = 
+    server->backup->replaced =
       silc_calloc(1, sizeof(*server->backup->replaced));
     server->backup->replaced_count = 1;
   }
 
-  SILC_LOG_DEBUG(("********************************"));
-  SILC_LOG_DEBUG(("Replaced added"));
+  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++) {
@@ -203,35 +254,31 @@ void silc_server_backup_replaced_add(SilcServer server,
    and the bacup router entry to the `server' pointer if non-NULL. Returns
    FALSE if the router is not replaced by backup router. */
 
-bool silc_server_backup_replaced_get(SilcServer server,
-                                    SilcServerID *server_id,
-                                    SilcServerEntry *server_entry)
+SilcBool silc_server_backup_replaced_get(SilcServer server,
+                                        SilcServerID *server_id,
+                                        SilcServerEntry *server_entry)
 {
   int i;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  SILC_LOG_DEBUG(("*************************************"));
-
   if (!server->backup || !server->backup->replaced)
     return FALSE;
 
   for (i = 0; i < server->backup->replaced_count; i++) {
     if (!server->backup->replaced[i])
       continue;
-    SILC_LOG_HEXDUMP(("IP"), server_id->ip.data, server_id->ip.data_len);
-    SILC_LOG_HEXDUMP(("IP"), server->backup->replaced[i]->ip.data, 
-                    server->backup->replaced[i]->ip.data_len);
-    if (!memcmp(&server->backup->replaced[i]->ip, &server_id->ip.data,
+    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(("REPLACED"));
+      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(("NOT REPLACED"));
+  SILC_LOG_DEBUG(("Router %s is not replaced by backup router",
+                 silc_id_render(server_id, SILC_ID_SERVER)));
   return FALSE;
 }
 
@@ -242,8 +289,6 @@ void silc_server_backup_replaced_del(SilcServer server,
 {
   int i;
 
-  SILC_LOG_DEBUG(("Start"));
-
   if (!server->backup || !server->backup->replaced)
     return;
 
@@ -253,24 +298,21 @@ void silc_server_backup_replaced_del(SilcServer server,
     if (server->backup->replaced[i]->server == server_entry) {
       silc_free(server->backup->replaced[i]);
       server->backup->replaced[i] = NULL;
-      return;
     }
   }
 }
 
-/* Broadcast the received packet indicated by `packet' to all of our backup 
-   routers. All router wide information is passed using broadcast packets. 
+/* Broadcast the received packet indicated by `packet' to all of our backup
+   routers. All router wide information is passed using broadcast packets.
    That is why all backup routers need to get this data too. It is expected
    that the caller already knows that the `packet' is broadcast packet. */
 
-void silc_server_backup_broadcast(SilcServer server, 
-                                 SilcSocketConnection sender,
-                                 SilcPacketContext *packet)
+void silc_server_backup_broadcast(SilcServer server,
+                                 SilcPacketStream sender,
+                                 SilcPacket packet)
 {
   SilcServerEntry backup;
-  SilcSocketConnection sock;
-  SilcBuffer buffer;
-  SilcIDListData idata;
+  SilcPacketStream sock;
   int i;
 
   if (!server->backup || server->server_type != SILC_ROUTER)
@@ -278,29 +320,17 @@ void silc_server_backup_broadcast(SilcServer server,
 
   SILC_LOG_DEBUG(("Broadcasting received packet to backup routers"));
 
-  buffer = packet->buffer;
-  silc_buffer_push(buffer, buffer->data - buffer->head);
-
   for (i = 0; i < server->backup->servers_count; i++) {
     backup = server->backup->servers[i].server;
 
-    if (!backup || backup->connection == sender || 
+    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, idata->psn_send++, 
-                       sock->outbuf, sock->outbuf->len);
-
-    SILC_LOG_HEXDUMP(("Broadcasted packet, len %d", sock->outbuf->len),
-                    sock->outbuf->data, sock->outbuf->len);
-
-    /* Now actually send the packet */
-    silc_server_packet_send_real(server, sock, FALSE);
+    silc_server_packet_route(server, sock, packet);
   }
 }
 
@@ -319,33 +349,30 @@ void silc_server_backup_send(SilcServer server,
                             SilcPacketType type,
                             SilcPacketFlags flags,
                             unsigned char *data,
-                            uint32 data_len,
-                            bool force_send,
-                            bool local)
+                            SilcUInt32 data_len,
+                            SilcBool force_send,
+                            SilcBool local)
 {
   SilcServerEntry backup;
-  SilcSocketConnection sock;
+  SilcPacketStream sock;
   int i;
 
   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_server_packet_send(server, backup->connection, type, flags, 
-                           data, data_len, force_send);
+
+    silc_server_packet_send(server, backup->connection, type, flags,
+                           data, data_len);
   }
 }
 
@@ -361,139 +388,288 @@ void silc_server_backup_send_dest(SilcServer server,
                                  void *dst_id,
                                  SilcIdType dst_id_type,
                                  unsigned char *data,
-                                 uint32 data_len,
-                                 bool force_send,
-                                 bool local)
+                                 SilcUInt32 data_len,
+                                 SilcBool force_send,
+                                 SilcBool local)
 {
   SilcServerEntry backup;
-  SilcSocketConnection sock;
+  SilcPacketStream sock;
   int i;
 
   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_server_packet_send_dest(server, backup->connection, type, flags, 
-                                dst_id, dst_id_type, data, data_len, 
-                                force_send);
+
+    silc_server_packet_send_dest(server, backup->connection, type, flags,
+                                dst_id, dst_id_type, data, data_len);
+  }
+}
+
+/* Send the START_USE indication to remote connection.  If `failure' is
+   TRUE then this sends SILC_PACKET_FAILURE.  Otherwise it sends
+   SILC_PACKET_RESUME_ROUTER. */
+
+void silc_server_backup_send_start_use(SilcServer server,
+                                      SilcPacketStream sock,
+                                      SilcBool failure)
+{
+  unsigned char data[4];
+
+  SILC_LOG_DEBUG(("Sending START_USE (%s)",
+                 failure ? "failure" : "success"));
+
+  if (failure) {
+    SILC_PUT32_MSB(SILC_SERVER_BACKUP_START_USE, data);
+    silc_server_packet_send(server, sock, SILC_PACKET_FAILURE, 0,
+                           data, 4);
+  } else {
+    data[0] = SILC_SERVER_BACKUP_START_USE;
+    data[1] = 0;
+    silc_server_packet_send(server, sock,
+                           SILC_PACKET_RESUME_ROUTER, 0,
+                           data, 2);
+  }
+}
+
+/* Send the REPLACED indication to remote router.  This is send by the
+   primary router (remote router) of the primary router that came back
+   online.  This is not sent by backup router or any other server. */
+
+void silc_server_backup_send_replaced(SilcServer server,
+                                     SilcPacketStream sock)
+{
+  unsigned char data[4];
+
+  SILC_LOG_DEBUG(("Sending REPLACED"));
+
+  data[0] = SILC_SERVER_BACKUP_REPLACED;
+  data[1] = 0;
+  silc_server_packet_send(server, sock,
+                         SILC_PACKET_RESUME_ROUTER, 0,
+                         data, 2);
+}
+
+
+/************************ Backup Resuming Protocol **************************/
+
+/* Timeout callback for protocol */
+
+SILC_TASK_CALLBACK(silc_server_backup_timeout)
+{
+  SilcServerBackupProtocolContext ctx = context;
+  SilcServer server = app_context;
+
+  SILC_LOG_INFO(("Timeout occurred during backup resuming protocol"));
+  ctx->timeout = TRUE;
+  ctx->error = TRUE;
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_protocol_backup_done, context,
+                                0, 0);
+}
+
+/* Callback to start the protocol as responder */
+
+SILC_TASK_CALLBACK(silc_server_backup_responder_start)
+{
+  SilcServerBackupProtocolContext proto_ctx = context;
+  SilcPacketStream sock = proto_ctx->sock;
+  SilcIDListData idata = silc_packet_get_context(sock);
+  SilcServer server = app_context;
+
+  /* If other protocol is executing at the same time, start with timeout. */
+  if (idata->sconn->op) {
+    SILC_LOG_DEBUG(("Other protocol is executing, wait for it to finish"));
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_backup_responder_start,
+                                  proto_ctx, 2, 0);
+    return;
+  }
+
+  /* Register protocol timeout */
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_backup_timeout,
+                                proto_ctx, 30, 0);
+
+  /* Run the backup resuming protocol */
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_protocol_backup,
+                                proto_ctx, 0, 0);
+}
+
+/* Callback to send START_USE to backup to check whether using backup
+   is ok. */
+
+SILC_TASK_CALLBACK(silc_server_backup_check_status)
+{
+  SilcPacketStream 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_packet_stream_unref(sock);
+}
+
+typedef struct {
+  SilcServer server;
+  SilcPacketStream sock;
+  SilcPacket packet;
+} *SilcServerBackupPing;
+
+/* PING command reply callback */
+
+void silc_server_backup_ping_reply(void *context, void *reply)
+{
+  SilcServerBackupPing pc = context;
+  SilcServerCommandReplyContext cmdr = reply;
+
+  if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
+    /* Timeout error occurred, the primary is really down. */
+    SilcPacketStream primary = SILC_PRIMARY_ROUTE(pc->server);
+
+    SILC_LOG_DEBUG(("PING timeout, primary is down"));
+
+    if (primary) {
+      silc_server_free_sock_user_data(pc->server, primary, NULL);
+      silc_server_close_connection(pc->server, primary);
+    }
+
+    /* Reprocess the RESUME_ROUTER packet */
+    silc_server_backup_resume_router(pc->server, pc->sock, pc->packet);
+  } else {
+    /* The primary is not down, refuse to serve the server as primary */
+    SILC_LOG_DEBUG(("PING received, primary is up"));
+    silc_server_backup_send_start_use(pc->server, pc->sock, TRUE);
+    silc_packet_free(pc->packet);
   }
+
+  silc_packet_stream_unref(pc->sock);
+  silc_free(pc);
 }
 
 /* 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. */
 
-void silc_server_backup_resume_router(SilcServer server, 
-                                     SilcSocketConnection sock, 
-                                     SilcPacketContext *packet)
+void silc_server_backup_resume_router(SilcServer server,
+                                     SilcPacketStream sock,
+                                     SilcPacket packet)
 {
-  uint8 type, session;
+  SilcIDListData idata = silc_packet_get_context(sock);
+  SilcServerEntry router = (SilcServerEntry)idata;
+  SilcUInt8 type, session;
   SilcServerBackupProtocolContext ctx;
   int i, ret;
 
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      sock->type == SILC_SOCKET_TYPE_UNKNOWN)
-    return;
+  SILC_LOG_DEBUG(("Received RESUME_ROUTER packet"));
 
-  SILC_LOG_DEBUG(("Start"));
+  if (idata->conn_type == SILC_CONN_CLIENT ||
+      idata->conn_type == SILC_CONN_UNKNOWN) {
+    SILC_LOG_DEBUG(("Bad packet received"));
+    silc_packet_free(packet);
+    return;
+  }
 
-  ret = silc_buffer_unformat(packet->buffer,
+  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"));
+    silc_packet_free(packet);
     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) {
-    ctx = sock->protocol->context;
-    ctx->type = type;
-
-    SILC_LOG_DEBUG(("********************************"));
-    SILC_LOG_DEBUG(("Continuing protocol, type %d", type));
-
-    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);
+  /* Check whether this packet is used to tell us that server will start
+     using us as primary router. */
+  if (type == SILC_SERVER_BACKUP_START_USE) {
+    SilcBuffer idp;
+    SilcServerBackupPing pc;
+
+    /* If we are normal server then backup router has sent us back
+       this reply and we use the backup as primary router now. */
+    if (server->server_type == SILC_SERVER) {
+      /* Nothing to do here actually, since we have switched already. */
+      SILC_LOG_DEBUG(("Received successful START_USE from backup router"));
+      silc_packet_free(packet);
       return;
     }
 
-    SILC_LOG_DEBUG(("Bad resume router packet"));
-    return;
-  }
+    /* Backup router following. */
 
-  /* We don't have protocol active. If we are router and the packet is 
-     coming from our primary router then lets check whether it means we've
-     been replaced by an backup router in my cell. This is usually received
-     immediately after we've connected to our primary router. */
-
-  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
-      server->router == sock->user_data &&
-      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;
+    /* If we are marked as router then the primary is down and we send
+       success START_USE back to the server. */
+    if (server->server_type == SILC_ROUTER) {
+      SILC_LOG_DEBUG(("Sending success START_USE back"));
+      silc_server_backup_send_start_use(server, sock, FALSE);
+      silc_packet_free(packet);
+      return;
+    }
 
-    SILC_LOG_INFO(("We are replaced by an backup router in this cell, will "
-                  "wait until backup resuming protocol is executed"));
+    /* We have just lost primary, send success START_USE back */
+    if (server->standalone) {
+      SILC_LOG_DEBUG(("We are stanalone, sending success START_USE back"));
+      silc_server_backup_send_start_use(server, sock, FALSE);
+      silc_packet_free(packet);
+      return;
+    }
 
-    SILC_LOG_DEBUG(("We are replaced by an backup router in this cell"));
-    idata->status |= SILC_IDLIST_STATUS_DISABLED;
+    /* We are backup router. This server claims that our primary is down.
+       We will check this ourselves by sending PING command to the primary. */
+    SILC_LOG_DEBUG(("Sending PING to detect status of primary router"));
+    idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+    silc_server_send_command(server, SILC_PRIMARY_ROUTE(server),
+                            SILC_COMMAND_PING, ++server->cmd_ident, 1,
+                            1, idp->data, silc_buffer_len(idp));
+    silc_buffer_free(idp);
+
+    /* Reprocess this packet after received reply from router */
+    pc = silc_calloc(1, sizeof(*pc));
+    pc->server = server;
+    pc->sock = sock;
+    pc->packet = packet;
+    silc_packet_stream_ref(sock);
+    silc_server_command_pending_timed(server, SILC_COMMAND_PING,
+                                     server->cmd_ident,
+                                     silc_server_backup_ping_reply, pc, 15);
     return;
   }
 
-  if (type == SILC_SERVER_BACKUP_START ||
-      type == SILC_SERVER_BACKUP_START_GLOBAL) {
-    /* We have received a start for resuming protocol. */
+  /* Start the resuming protocol if requested. */
+  if (type == SILC_SERVER_BACKUP_START) {
+    /* We have received a start for resuming protocol.  We are either
+       primary router that came back online or normal server. */
     SilcServerBackupProtocolContext proto_ctx;
 
+    /* If backup had closed the connection earlier we won't allow resuming
+       since we (primary router) have never gone away. */
+    if (server->server_type == SILC_ROUTER && !server->backup_router &&
+       server->backup_closed) {
+      unsigned char data[4];
+      SILC_LOG_DEBUG(("Backup resuming not allowed since we are still "
+                     "primary router"));
+      SILC_LOG_INFO(("Backup resuming not allowed since we are still "
+                    "primary router"));
+      SILC_PUT32_MSB(SILC_SERVER_BACKUP_START, data);
+      silc_server_packet_send(server, sock, SILC_PACKET_FAILURE, 0,
+                             data, 4);
+      server->backup_closed = FALSE;
+      silc_packet_free(packet);
+      return;
+    }
+
     proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
     proto_ctx->server = server;
     proto_ctx->sock = sock;
@@ -501,81 +677,127 @@ void silc_server_backup_resume_router(SilcServer server,
     proto_ctx->type = type;
     proto_ctx->session = session;
     proto_ctx->start = time(0);
+    silc_packet_stream_ref(sock);
+    router->backup = TRUE;
+    router->backup_proto = proto_ctx;
 
     SILC_LOG_DEBUG(("Starting backup resuming protocol as responder"));
+    SILC_LOG_INFO(("Starting backup resuming protocol"));
+
+    /* Start protocol immediately */
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_backup_responder_start,
+                                  proto_ctx, 0, 1);
+    return;
+  }
 
-    /* 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);
+  /* If we are router and the packet is coming from our primary router
+     then it means we have been replaced by an backup router in our cell. */
+  if (type == SILC_SERVER_BACKUP_REPLACED &&
+      server->server_type == SILC_ROUTER &&
+      idata->conn_type == SILC_CONN_ROUTER &&
+      SILC_PRIMARY_ROUTE(server) == sock) {
+    /* 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. */
+    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;
+    silc_packet_free(packet);
+    return;
   }
-}
 
-/* Timeout task callback to connect to remote router */
+  /* Activate the shared protocol context for this socket connection
+     if necessary */
+  if (type == SILC_SERVER_BACKUP_RESUMED &&
+      idata->conn_type == SILC_CONN_ROUTER && !router->backup &&
+      idata->status & SILC_IDLIST_STATUS_DISABLED) {
+    SilcServerEntry backup_router;
 
-SILC_TASK_CALLBACK(silc_server_backup_connect_to_router)
-{
-  SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
-  int sock;
-
-  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,
-                                   sconn->remote_host);
-  if (sock < 0) {
-    silc_schedule_task_add(server->schedule, 0,
-                          silc_server_backup_connect_to_router,
-                          context, 5, 0, SILC_TASK_TIMEOUT, 
-                          SILC_TASK_PRI_NORMAL);
-    return;
+    if (silc_server_backup_replaced_get(server, router->id, &backup_router)) {
+      ctx = backup_router->backup_proto;
+      if (ctx->sock)
+       silc_packet_stream_unref(ctx->sock);
+      router->backup = TRUE;
+      router->backup_proto = ctx;
+      ctx->sock = sock;
+      silc_packet_stream_ref(sock);
+    }
   }
 
-  /* Continue with key exchange protocol */
-  silc_server_start_key_exchange(server, sconn, sock);
-}
+  /* Call the resuming protocol if the protocol is active. */
+  if (router->backup) {
+    ctx = router->backup_proto;
+    ctx->type = type;
 
-/* Constantly tries to reconnect to a primary router indicated by the
-   `ip' and `port'. The `connected' callback will be called when the
-   connection is created. */
+    for (i = 0; i < ctx->sessions_count; i++) {
+      if (session == ctx->sessions[i].session) {
+       ctx->session = session;
+       silc_schedule_task_add_timeout(server->schedule,
+                                      silc_server_protocol_backup,
+                                      ctx, 0, 1);
+       silc_packet_free(packet);
+       return;
+      }
+    }
 
-void silc_server_backup_reconnect(SilcServer server,
-                                 const char *ip, uint16 port,
-                                 SilcServerConnectRouterCallback callback,
-                                 void *context)
-{
-  SilcServerConnection sconn;
-
-  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;
-  silc_schedule_task_add(server->schedule, 0, 
-                        silc_server_backup_connect_to_router,
-                        sconn, 1, 0, SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+    /* If RESUMED received the session ID is zero, execute the protocol. */
+    if (type == SILC_SERVER_BACKUP_RESUMED) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_protocol_backup,
+                                    ctx, 0, 1);
+      silc_packet_free(packet);
+      return;
+    }
+
+    SILC_LOG_ERROR(("Unknown backup resuming session %d", session));
+    silc_packet_free(packet);
+    return;
+  }
+
+  silc_packet_free(packet);
 }
 
+/* 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 = 
+  SilcServerBackupProtocolContext proto_ctx =
     (SilcServerBackupProtocolContext)context;
   SilcServer server = proto_ctx->server;
-  SilcSocketConnection sock = proto_ctx->sock;
 
   SILC_LOG_DEBUG(("Starting backup resuming protocol as initiator"));
+  SILC_LOG_INFO(("Starting backup resuming protocol"));
+
+  /* Register protocol timeout */
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_backup_timeout,
+                                proto_ctx, 30, 0);
 
   /* 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_timeout(server->schedule,
+                                silc_server_protocol_backup,
+                                proto_ctx, 0, 0);
+}
+
+SILC_TASK_CALLBACK(silc_server_backup_connected_again)
+{
+  SilcServer server = app_context;
+  SilcServerConfigRouter *primary;
+
+  if (server->server_shutdown)
+    return;
+
+  primary = silc_server_config_get_primary_router(server);
+  if (primary) {
+    if (!silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+                                        primary->host, primary->port))
+      silc_server_create_connection(server, FALSE, FALSE,
+                                   primary->host, primary->port,
+                                   silc_server_backup_connected,
+                                   context);
+  }
 }
 
 /* Called when we've established connection back to our primary router
@@ -587,21 +809,45 @@ void silc_server_backup_connected(SilcServer server,
                                  void *context)
 {
   SilcServerBackupProtocolContext proto_ctx;
-  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
+  SilcPacketStream sock;
+
+  if (!server_entry) {
+    /* Try again */
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_backup_connected_again,
+                                  context, 5, 0);
+    return;
+  }
 
+  sock = server_entry->connection;
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = server;
   proto_ctx->sock = sock;
   proto_ctx->responder = FALSE;
   proto_ctx->type = SILC_SERVER_BACKUP_START;
   proto_ctx->start = time(0);
+  silc_packet_stream_ref(sock);
 
   /* Start through scheduler */
-  silc_schedule_task_add(server->schedule, 0,
-                        silc_server_backup_connected_later,
-                        proto_ctx, 0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_backup_connected_later,
+                                proto_ctx, 0, 1);
+}
+
+SILC_TASK_CALLBACK(silc_server_backup_connect_primary_again)
+{
+  SilcServer server = app_context;
+  SilcServerConfigRouter *primary;
+
+  primary = silc_server_config_get_primary_router(server);
+  if (primary) {
+    if (!silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+                                        primary->host, primary->port))
+      silc_server_create_connection(server, FALSE, FALSE,
+                                   primary->host, primary->port,
+                                   silc_server_backup_connect_primary,
+                                   context);
+  }
 }
 
 /* Called when normal server has connected to its primary router after
@@ -613,29 +859,39 @@ static void silc_server_backup_connect_primary(SilcServer server,
                                               SilcServerEntry server_entry,
                                               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;
-  SilcBuffer buffer;
+  SilcPacketStream backup_router = context;
+  SilcIDListData idata = silc_packet_get_context(backup_router);
+  SilcServerEntry router = (SilcServerEntry)idata;
+  SilcServerBackupProtocolContext ctx;
+  SilcPacketStream sock;
+  unsigned char data[2];
+
+  if (!server_entry) {
+    /* Try again */
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_backup_connect_primary_again,
+                                  context, 0, 0);
+    return;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  if (!router->backup || !server_entry->connection) {
+    silc_packet_stream_unref(backup_router);
+    return;
+  }
+
+  ctx = router->backup_proto;
+  sock = server_entry->connection;
+  idata = (SilcIDListData)server_entry;
 
-  SILC_LOG_DEBUG(("********************************"));
-  SILC_LOG_DEBUG(("Sending CONNECTED packet, session %d", ctx->session));
+  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));
-  silc_buffer_format(buffer,
-                    SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_CONNECTED),
-                    SILC_STR_UI_CHAR(ctx->session),
-                    SILC_STR_END);
-  silc_server_packet_send(server, backup_router, 
-                         SILC_PACKET_RESUME_ROUTER, 0, 
-                         buffer->data, buffer->len, FALSE);
-  silc_buffer_free(buffer);
+  data[0] = SILC_SERVER_BACKUP_CONNECTED;
+  data[1] = ctx->session;
+  silc_server_packet_send(server, backup_router,
+                         SILC_PACKET_RESUME_ROUTER, 0, data, 2);
 
   /* The primary connection is disabled until it sends the RESUMED packet
      to us. */
@@ -643,598 +899,584 @@ static void silc_server_backup_connect_primary(SilcServer server,
 
   /* 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 
+     packets in this protocol. We don't talk with backup router
      anymore. */
-  sock->protocol = backup_router->protocol;
-  ctx->sock = (SilcSocketConnection)server_entry->connection;
-  backup_router->protocol = NULL;
+  if (ctx->sock)
+    silc_packet_stream_unref(ctx->sock);
+  ctx->sock = sock;
+  silc_packet_stream_ref(sock);
+  server_entry->backup = TRUE;
+  server_entry->backup_proto = ctx;
+  router->backup = FALSE;
+  router->backup_proto = NULL;
+
+  /* Unref */
+  silc_packet_stream_unref(backup_router);
 }
 
+/* Timeout callback used by the backup router to send the ENDING packet
+   to primary router to indicate that it can now resume as being primary
+   router. All CONNECTED packets has been received when we reach this. */
+
 SILC_TASK_CALLBACK(silc_server_backup_send_resumed)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServerBackupProtocolContext ctx = context;
   SilcServer server = ctx->server;
-  SilcBuffer packet;
+  unsigned char data[2];
   int i;
 
+  SILC_LOG_DEBUG(("Start"));
+
   for (i = 0; i < ctx->sessions_count; i++)
-    if (ctx->sessions[i].server_entry == ctx->sock->user_data)
+    if (ctx->sessions[i].server_entry == silc_packet_get_context(ctx->sock))
       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;
-}
-
-/* Resume protocol with RESUME_ROUTER packet: 
-
-   SILC_PACKET_RESUME_ROUTER:
+  data[0] = SILC_SERVER_BACKUP_ENDING;
+  data[1] = ctx->session;
+  silc_server_packet_send(server, ctx->sock, SILC_PACKET_RESUME_ROUTER, 0,
+                         data, sizeof(data));
 
-   <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.
-
- */
+  /* The protocol will go to END state. */
+  ctx->state = 250;
+}
 
 /* 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)
+SILC_TASK_CALLBACK(silc_server_protocol_backup)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServerBackupProtocolContext ctx = context;
   SilcServer server = ctx->server;
-  SilcBuffer packet;
-  SilcIDCacheList list;
-  SilcIDCacheEntry id_cache;
-  SilcServerEntry server_entry;
+  SilcServerEntry server_entry = NULL;
+  SilcPacketStream sock = NULL;
+  unsigned char data[2];
+  SilcDList list;
   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));
+  if (!ctx->state)
+    ctx->state = 1;
 
-  switch(protocol->state) {
-  case SILC_PROTOCOL_STATE_START:
+  switch(ctx->state) {
+  case 1:
     if (ctx->responder == FALSE) {
-      /* Initiator of the protocol. We are backup router */
-
-      packet = silc_buffer_alloc(2);
-      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-
-      SILC_LOG_DEBUG(("********************************"));
-      SILC_LOG_DEBUG(("Sending START packets"));
-
-      /* Send the START packet to primary router and normal servers. */
-      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;
-           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(("********************************"));
-           SILC_LOG_DEBUG(("START (local) for session %d", 
-                           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);
-      }
-
-      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(("********************************"));
-           SILC_LOG_DEBUG(("START (global) for session %d", 
-                           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);
+      /*
+       * Initiator (backup router)
+       */
+
+      /* Send the START packet to primary router and normal servers. The
+        packet will indicate to the primary router that it has been replaced
+        by us.  For normal servers it means that we will be resigning as
+        being primary router shortly. */
+      list = silc_packet_engine_get_streams(server->packet_engine);
+      if (!list)
+       return;
+
+      silc_dlist_start(list);
+      while ((sock = silc_dlist_get(list))) {
+       server_entry = silc_packet_get_context(sock);
+
+       if (!server_entry || server_entry == server->id_entry ||
+           (server_entry->data.conn_type != SILC_CONN_ROUTER &&
+            server_entry->data.conn_type != SILC_CONN_SERVER))
+         continue;
+
+       if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
+         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 */
+       server_entry->backup = TRUE;
+       server_entry->backup_proto = ctx;
+
+       data[0] = SILC_SERVER_BACKUP_START;
+       data[1] = ctx->sessions_count;
+       silc_server_packet_send(server, sock, SILC_PACKET_RESUME_ROUTER, 0,
+                               data, sizeof(data));
+       ctx->sessions_count++;
       }
+      silc_packet_engine_free_streams_list(list);
 
-      silc_buffer_free(packet);
-
-      /* Announce all of our information */
+      /* Announce data to the new primary to be. */
       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;
+      ctx->state++;
 
-      /* 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"));
+    } else {
+      /*
+       * Responder (all servers and routers)
+       */
+      SilcServerConfigRouter *primary;
+
+      /* We should have received START packet */
+      if (ctx->type != SILC_SERVER_BACKUP_START) {
+       SILC_LOG_ERROR(("Bad resume router packet START %d", ctx->type));
        break;
       }
 
-      SILC_LOG_DEBUG(("********************************"));
-      SILC_LOG_DEBUG(("Received START packet, reconnecting to router"));
-
       /* 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 && server->backup_primary) {
-       silc_server_backup_reconnect(server,
-                                    primary->host, primary->port,
-                                    silc_server_backup_connect_primary,
-                                    ctx->sock);
+      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_CONN_ROUTER)) {
+       SILC_LOG_DEBUG(("Received START (session %d), reconnect to router",
+                       ctx->session));
+       silc_packet_stream_ref(ctx->sock);
+       silc_server_create_connection(server, FALSE, FALSE,
+                                     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));
 
-       SILC_LOG_DEBUG(("********************************"));
-       SILC_LOG_DEBUG(("Sending CONNECTED packet, session %d", 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);
+       data[0] = SILC_SERVER_BACKUP_CONNECTED;
+       data[1] = ctx->session;
+       silc_server_packet_send(server, ctx->sock,
+                               SILC_PACKET_RESUME_ROUTER, 0,
+                               data, sizeof(data));
       }
 
-      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;
-
+      /* Add this resuming session */
       ctx->sessions = silc_realloc(ctx->sessions,
                                   sizeof(*ctx->sessions) *
                                   (ctx->sessions_count + 1));
       ctx->sessions[ctx->sessions_count].session = ctx->session;
       ctx->sessions_count++;
+
+      /* Normal server goes directly to the END state. */
+      if (server->server_type == SILC_ROUTER &&
+         (!server->router ||
+          server->router->data.status & SILC_IDLIST_STATUS_DISABLED))
+       ctx->state++;
+      else
+       ctx->state = 250;
     }
     break;
 
   case 2:
     if (ctx->responder == FALSE) {
-      /* Initiator */
+      /*
+       * Initiator (backup router)
+       */
 
       /* 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;
       }
 
-      SILC_LOG_DEBUG(("********************************"));
-      SILC_LOG_DEBUG(("Received CONNECTED packet, session %d", ctx->session));
-
       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;
        }
       }
 
+      /* See if all returned CONNECTED, if not, then continue waiting. */
       for (i = 0; i < ctx->sessions_count; i++) {
        if (!ctx->sessions[i].connected)
          return;
       }
 
-      SILC_LOG_DEBUG(("********************************"));
-      SILC_LOG_DEBUG(("Sending ENDING packet to primary"));
+      SILC_LOG_INFO(("All sessions have returned CONNECTED packets, "
+                    "continuing"));
+      SILC_LOG_DEBUG(("Sending ENDING packet to primary router"));
 
-      /* 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);
+      /* The ENDING is sent with timeout, and then we continue to the
+        END state in the protocol. */
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_backup_send_resumed,
+                                    ctx, 1, 0);
       return;
+
     } else {
-      /* Responder */
+      /*
+       * Responder (primary router)
+       */
 
       /* 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(("********************************"));
-      SILC_LOG_DEBUG(("Received ENDING packet, sending RESUMED packets"));
-
-      /* 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 &&
-         !(server->router->data.status & SILC_IDLIST_STATUS_DISABLED) &&
-         silc_server_config_is_primary_route(server->config)) {
-       /* We'll wait for RESUMED packet */
-       protocol->state = SILC_PROTOCOL_STATE_END;
-       break;
-      }
+      SILC_LOG_DEBUG(("Received ENDING packet, we are going to resume now"));
 
       /* Switch announced informations to our primary router of using the
         backup router. */
-      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, FALSE);
-      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));
-
-      /* We are the primary router, start sending RESUMED packets. */
-      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;
-           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(("********************************"));
-           SILC_LOG_DEBUG(("RESUMED packet (local)"));
-
-           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);
+      silc_server_local_servers_toggle_enabled(server, TRUE);
+      silc_server_update_servers_by_server(server,
+                                          silc_packet_get_context(ctx->sock),
+                                          server->router);
+      silc_server_update_clients_by_server(server,
+                                          silc_packet_get_context(ctx->sock),
+                                          server->router, TRUE);
+
+      /* We as primary router now must send RESUMED packets to all servers
+        and routers so that they know we are back.   For backup router we
+        send the packet last so that we give the backup as much time as
+        possible to deal with message routing at this critical moment. */
+      list = silc_packet_engine_get_streams(server->packet_engine);
+      if (!list)
+       return;
+
+      silc_dlist_start(list);
+      while ((sock = silc_dlist_get(list))) {
+       server_entry = silc_packet_get_context(sock);
+
+       if (!server_entry || server_entry == server->id_entry ||
+           (server_entry->data.conn_type != SILC_CONN_ROUTER &&
+            server_entry->data.conn_type != SILC_CONN_SERVER))
+         continue;
+
+       /* Send to backup last */
+       if (sock == ctx->sock)
+         continue;
+
+       server_entry = silc_packet_get_context(sock);
+       server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+       SILC_LOG_DEBUG(("Sending RESUMED to %s", server_entry->server_name));
+       SILC_LOG_INFO(("Sending RESUMED to %s", server_entry->server_name));
+
+       /* This connection is performing this protocol too now */
+       server_entry->backup = TRUE;
+       server_entry->backup_proto = ctx;
+
+       data[0] = SILC_SERVER_BACKUP_RESUMED;
+       data[1] = 0;
+       silc_server_packet_send(server, sock, SILC_PACKET_RESUME_ROUTER, 0,
+                               data, sizeof(data));
       }
 
-      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(("********************************"));
-           SILC_LOG_DEBUG(("RESUMED packet (global)"));
+      /* Now send the same packet to backup */
+      if (sock != ctx->sock) {
+       sleep(1);
+       sock = ctx->sock;
+       server_entry = silc_packet_get_context(sock);
+       server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
 
-           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+       SILC_LOG_DEBUG(("Sending RESUMED to %s", server_entry->server_name));
+       SILC_LOG_INFO(("Sending RESUMED to %s", server_entry->server_name));
 
-           /* 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;
-         }
-       }
+       /* This connection is performing this protocol too now */
+       server_entry->backup = TRUE;
+       server_entry->backup_proto = ctx;
 
-       silc_idcache_list_free(list);
+       data[0] = SILC_SERVER_BACKUP_RESUMED;
+       data[1] = 0;
+       silc_server_packet_send(server, sock, SILC_PACKET_RESUME_ROUTER, 0,
+                               data, sizeof(data));
       }
+      silc_packet_engine_free_streams_list(list);
 
-      silc_buffer_free(packet);
-
+      /* We are now resumed and are back as primary router in the cell. */
       SILC_LOG_INFO(("We are now the primary router of our cell again"));
+      server->wait_backup = FALSE;
+
+      /* Announce WATCH list a little later */
+      silc_packet_stream_ref(ctx->sock);
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_backup_announce_watches,
+                                    ctx->sock, 4, 0);
 
       /* For us this is the end of this protocol. */
-      if (protocol->final_callback)
-       silc_protocol_execute_final(protocol, server->schedule);
-      else
-       silc_protocol_free(protocol);
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_protocol_backup_done,
+                                    ctx, 0, 1);
     }
     break;
 
-  case SILC_PROTOCOL_STATE_END:
+  case 250:
     {
-      SilcIDListData idata;
+      /*
+       * Responder (backup router, servers, and remote 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"));
+      /* We should have been received RESUMED from our primary router. */
+      if (ctx->type != SILC_SERVER_BACKUP_RESUMED) {
+       SILC_LOG_ERROR(("Bad resume router packet RESUMED %d", ctx->type));
        break;
       }
 
-      SILC_LOG_DEBUG(("********************************"));
-      SILC_LOG_DEBUG(("Received RESUMED packet"));
+      SILC_LOG_INFO(("Received RESUMED from new primary router"));
 
-      /* We have now new primary router. All traffic goes there from now on. */
+      /* If we are the backup router, mark that we are no longer primary
+        but are back to backup router status. */
       if (server->backup_router)
        server->server_type = SILC_BACKUP_ROUTER;
 
-      router = (SilcServerEntry)ctx->sock->user_data;
-      if (silc_server_backup_replaced_get(server, router->id, 
+      /* We have now new primary router. All traffic goes there from now on. */
+      router = silc_packet_get_context(ctx->sock);
+      if (silc_server_backup_replaced_get(server, router->id,
                                          &backup_router)) {
 
        if (backup_router == server->router) {
+         /* 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));
-         SILC_LOG_DEBUG(("Switching back to primary router %s",
-                         server->router->server_name));
-         idata = (SilcIDListData)server->router;
-         idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
        } else {
-         SILC_LOG_INFO(("Resuming the use of router %s",
+         /* We are connected to new primary and now continue using it */
+         SILC_LOG_INFO(("Resuming the use of primary router %s",
                         router->server_name));
-         SILC_LOG_DEBUG(("Resuming the use of router %s",
-                         router->server_name));
-         idata = (SilcIDListData)router;
-         idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
        }
+       server->backup_primary = FALSE;
+       sock = router->connection;
 
-       /* Update the client entries of the backup router to the new 
+       /* 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, backup_router,
-                                            router, TRUE, FALSE);
+       silc_server_update_clients_by_server(
+                                  server, NULL, router,
+                                  server->server_type == SILC_BACKUP_ROUTER);
        if (server->server_type == SILC_SERVER)
          silc_server_update_channels_by_server(server, backup_router, router);
        silc_server_backup_replaced_del(server, backup_router);
-       silc_server_backup_add(server, backup_router, 
-                              ctx->sock->ip, ctx->sock->port,
-                              backup_router->server_type != SILC_ROUTER ?
-                              TRUE : FALSE);
-
-       /* Announce all of our information to the router. */
-       if (server->server_type == SILC_ROUTER)
-         silc_server_announce_servers(server, FALSE, 0, router->connection);
-
-       /* Announce our clients and channels to the router */
-       silc_server_announce_clients(server, 0, router->connection);
-       silc_server_announce_channels(server, 0, 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);
-      else
-       silc_protocol_free(protocol);
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_protocol_backup_done,
+                                    ctx, 0, 1);
     }
     break;
 
-  case SILC_PROTOCOL_STATE_ERROR:
+  case 251:
     /* Protocol has ended, call the final callback */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, server->schedule);
-    else
-      silc_protocol_free(protocol);
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_protocol_backup_done,
+                                  ctx, 0, 1);
     break;
 
-  case SILC_PROTOCOL_STATE_FAILURE:
+  case 252:
     /* Protocol has ended, call the final callback */
-    if (protocol->final_callback)
-      silc_protocol_execute_final(protocol, server->schedule);
-    else
-      silc_protocol_free(protocol);
+    SILC_LOG_ERROR(("Error during backup resume: received Failure"));
+    ctx->received_failure = TRUE;
+    silc_schedule_task_add_timeout(server->schedule,
+                                  silc_server_protocol_backup_done,
+                                  ctx, 0, 1);
     break;
 
-  case SILC_PROTOCOL_STATE_UNKNOWN:
+  default:
     break;
   }
 }
 
+/* Final resuming protocol completion callback */
+
 SILC_TASK_CALLBACK(silc_server_protocol_backup_done)
 {
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServerBackupProtocolContext ctx = context;
   SilcServer server = ctx->server;
+  SilcDList list;
   SilcServerEntry server_entry;
-  SilcSocketConnection sock;
-  SilcIDCacheList list;
-  SilcIDCacheEntry id_cache;
+  SilcPacketStream sock;
+  SilcBool error;
 
-  SILC_LOG_DEBUG(("Start"));
+  silc_schedule_task_del_by_context(server->schedule, ctx);
+
+  error = ctx->error;
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
-      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+  if (error)
     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;
+  list = silc_packet_engine_get_streams(server->packet_engine);
+  if (!list)
+    return;
 
-       if (sock->protocol == protocol) {
-         sock->protocol = NULL;
+  silc_dlist_start(list);
+  while ((sock = silc_dlist_get(list))) {
+    server_entry = silc_packet_get_context(sock);
+    if (!server_entry)
+      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 (server_entry->data.conn_type != SILC_CONN_ROUTER &&
+       server_entry->data.conn_type != SILC_CONN_SERVER)
+      continue;
 
-  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 (server_entry->backup_proto == ctx) {
+      if (error) {
 
-       if (sock->protocol == protocol) {
-         sock->protocol = NULL;
+       if (server->server_type == SILC_SERVER &&
+           server_entry->server_type == SILC_ROUTER)
+         continue;
+
+       /* Backup router */
+       if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router) {
+         if (ctx->sock == sock) {
+           silc_packet_stream_unref(sock);
+           ctx->sock = NULL;
+         }
 
-         if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
+         /* If failed after 10 attempts, it won't work, give up */
+         if (ctx->initiator_restart > 10)
+           ctx->received_failure = TRUE;
+
+         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 = sock;
+           proto_ctx->responder = FALSE;
+           proto_ctx->type = SILC_SERVER_BACKUP_START;
+           proto_ctx->start = time(0);
+           proto_ctx->initiator_restart = ctx->initiator_restart + 1;
+           silc_packet_stream_ref(sock);
+
+           /* Start through scheduler */
+           silc_schedule_task_add_timeout(server->schedule,
+                                          silc_server_backup_connected_later,
+                                          proto_ctx, 5, 0);
+         } 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);
            server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+           silc_server_update_servers_by_server(server, server->id_entry,
+                                                silc_packet_get_context(sock));
+           silc_server_update_clients_by_server(server, NULL,
+                                                silc_packet_get_context(sock),
+                                                TRUE);
+
+           /* Announce our clients and channels to the router */
+           silc_server_announce_clients(server, 0, sock);
+           silc_server_announce_channels(server, 0, sock);
+
+           /* Announce WATCH list a little later */
+           silc_packet_stream_ref(sock);
+           silc_schedule_task_add_timeout(server->schedule,
+                                          silc_server_backup_announce_watches,
+                                          sock, 5, 0);
+         }
+
+         continue;
        }
-       
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
+      }
+
+      server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+    }
+  }
+  silc_packet_engine_free_streams_list(list);
+
+  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, 0,
+                                    server->router->connection);
+
+      /* Announce our clients and channels to the router */
+      silc_server_announce_clients(server, 0, server->router->connection);
+      silc_server_announce_channels(server, 0, server->router->connection);
+
+      /* Announce WATCH list a little later */
+      silc_packet_stream_ref(server->router->connection);
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_backup_announce_watches,
+                                    server->router->connection, 4, 0);
+    }
+  } 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_packet_stream_ref(server->router->connection);
+       silc_schedule_task_add_timeout(server->schedule,
+                                      silc_server_backup_check_status,
+                                      server->router->connection,
+                                      5, 1);
+       silc_packet_stream_ref(server->router->connection);
+       silc_schedule_task_add_timeout(server->schedule,
+                                      silc_server_backup_check_status,
+                                      server->router->connection,
+                                      20, 1);
+       silc_packet_stream_ref(server->router->connection);
+       silc_schedule_task_add_timeout(server->schedule,
+                                      silc_server_backup_check_status,
+                                      server->router->connection,
+                                      60, 1);
       }
     }
-    silc_idcache_list_free(list);
   }
 
-  if (ctx->sock->protocol)
-    ctx->sock->protocol = NULL;
-  silc_protocol_free(protocol);
+  if (ctx->sock) {
+    SilcServerEntry r = silc_packet_get_context(ctx->sock);
+    if (r) {
+      r->backup = FALSE;
+      r->backup_proto = NULL;
+    }
+    silc_packet_stream_unref(ctx->sock);
+  }
   silc_free(ctx->sessions);
   silc_free(ctx);
 }
+
+SILC_TASK_CALLBACK(silc_server_backup_announce_watches)
+{
+  SilcPacketStream sock = context;
+  SilcServer server = app_context;
+  if (silc_packet_stream_is_valid(sock))
+    silc_server_announce_watches(server, sock);
+  silc_packet_stream_unref(sock);
+}