updates.
[silc.git] / apps / silcd / server.c
index f3022c2dd544006ecd398a6a42f01c465e408e38..2e85203df1d7335de7d244086c677cfb91a385b4 100644 (file)
@@ -77,10 +77,8 @@ void silc_server_free(SilcServer server)
     SilcSimContext *sim;
 #endif
 
-    if (server->local_list)
-      silc_free(server->local_list);
-    if (server->global_list)
-      silc_free(server->global_list);
+    silc_free(server->local_list);
+    silc_free(server->global_list);
     if (server->rng)
       silc_rng_free(server->rng);
 
@@ -95,8 +93,7 @@ void silc_server_free(SilcServer server)
     silc_dlist_uninit(server->sim);
 #endif
 
-    if (server->params)
-      silc_free(server->params);
+    silc_free(server->params);
 
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
@@ -307,8 +304,19 @@ int silc_server_init(SilcServer server)
 
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
-  if (server->config->servers)
+  if (server->config->servers) {
+    SilcServerConfigSectionServerConnection *ptr = server->config->servers;
+
     server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       break;
+      }
+      ptr = ptr->next;
+    }
+  }
 
   /* Register the ID Cache purge task. This periodically purges the ID cache
      and removes the expired cache entries. */
@@ -447,6 +455,21 @@ void silc_server_daemonise(SilcServer server)
   }
 }
 
+/* The heart of the server. This runs the scheduler thus runs the server. 
+   When this returns the server has been stopped and the program will
+   be terminated. */
+
+void silc_server_run(SilcServer server)
+{
+  SILC_LOG_DEBUG(("Running server"));
+
+  SILC_LOG_INFO(("SILC Server started"));
+
+  /* Start the scheduler, the heart of the SILC server. When this returns
+     the program will be terminated. */
+  silc_schedule(server->schedule);
+}
+
 /* Stops the SILC server. This function is used to shutdown the server. 
    This is usually called after the scheduler has returned. After stopping 
    the server one should call silc_server_free. */
@@ -463,19 +486,71 @@ void silc_server_stop(SilcServer server)
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
-/* The heart of the server. This runs the scheduler thus runs the server. 
-   When this returns the server has been stopped and the program will
-   be terminated. */
+/* Function that is called when the network connection to a router has
+   been established.  This will continue with the key exchange protocol
+   with the remote router. */
 
-void silc_server_run(SilcServer server)
+void silc_server_start_key_exchange(SilcServer server,
+                                   SilcServerConnection sconn,
+                                   int sock)
 {
-  SILC_LOG_DEBUG(("Running server"));
+  SilcSocketConnection newsocket;
+  SilcProtocol protocol;
+  SilcServerKEInternalContext *proto_ctx;
+  void *context;
 
-  SILC_LOG_INFO(("SILC Server started"));
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
 
-  /* Start the scheduler, the heart of the SILC server. When this returns
-     the program will be terminated. */
-  silc_schedule(server->schedule);
+  /* Create socket connection for the connection. Even though we
+     know that we are connecting to a router we will mark the socket
+     to be unknown connection until we have executed authentication
+     protocol. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
+  newsocket->hostname = strdup(sconn->remote_host);
+  newsocket->ip = strdup(sconn->remote_host);
+  newsocket->port = sconn->remote_port;
+  sconn->sock = newsocket;
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->context = (void *)sconn;
+  proto_ctx->sock = newsocket;
+  proto_ctx->rng = server->rng;
+  proto_ctx->responder = FALSE;
+      
+  /* Perform key exchange protocol. silc_server_connect_to_router_second
+     will be called after the protocol is finished. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
+                     &protocol, proto_ctx,
+                     silc_server_connect_to_router_second);
+  newsocket->protocol = protocol;
+      
+  /* Register a timeout task that will be executed if the protocol
+     is not executed within set limit. */
+  proto_ctx->timeout_task = 
+    silc_schedule_task_add(server->schedule, sock, 
+                      silc_server_timeout_remote,
+                      server, server->params->protocol_timeout,
+                      server->params->protocol_timeout_usec,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_LOW);
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as 
+     well. However, this doesn't set the scheduler for outgoing traffic,
+     it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)server;
+  SILC_REGISTER_CONNECTION_FOR_IO(sock);
+  
+  /* Run the protocol */
+  silc_protocol_execute(protocol, server->schedule, 0, 0);
 }
 
 /* Timeout callback that will be called to retry connecting to remote
@@ -522,14 +597,14 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
 {    
   SilcServerConnection sconn = (SilcServerConnection)context;
   SilcServer server = sconn->server;
-  SilcSocketConnection newsocket;
-  SilcProtocol protocol;
-  SilcServerKEInternalContext *proto_ctx;
   int sock;
 
-  SILC_LOG_INFO(("Connecting to the router %s on port %d", 
+  SILC_LOG_INFO(("Connecting to the %s %s on port %d", 
+                (sconn->backup ? "backup router" : "router"), 
                 sconn->remote_host, sconn->remote_port));
 
+  server->router_connect = time(0);
+
   /* Connect to remote host */
   sock = silc_net_create_connection(server->config->listen_port->local_ip,
                                    sconn->remote_port, 
@@ -543,58 +618,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
     return;
   }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* Create socket connection for the connection. Even though we
-     know that we are connecting to a router we will mark the socket
-     to be unknown connection until we have executed authentication
-     protocol. */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-  server->sockets[sock] = newsocket;
-  newsocket->hostname = strdup(sconn->remote_host);
-  newsocket->ip = strdup(sconn->remote_host);
-  newsocket->port = sconn->remote_port;
-  sconn->sock = newsocket;
-
-  /* Allocate internal protocol context. This is sent as context
-     to the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->server = (void *)server;
-  proto_ctx->context = (void *)sconn;
-  proto_ctx->sock = newsocket;
-  proto_ctx->rng = server->rng;
-  proto_ctx->responder = FALSE;
-      
-  /* Perform key exchange protocol. silc_server_connect_to_router_second
-     will be called after the protocol is finished. */
-  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &protocol, proto_ctx,
-                     silc_server_connect_to_router_second);
-  newsocket->protocol = protocol;
-      
-  /* Register a timeout task that will be executed if the protocol
-     is not executed within set limit. */
-  proto_ctx->timeout_task = 
-    silc_schedule_task_add(server->schedule, sock, 
-                      silc_server_timeout_remote,
-                      server, server->params->protocol_timeout,
-                      server->params->protocol_timeout_usec,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
-     and sets that outgoing packets may be sent to this connection as 
-     well. However, this doesn't set the scheduler for outgoing traffic,
-     it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)server;
-  SILC_REGISTER_CONNECTION_FOR_IO(sock);
-  
-  /* Run the protocol */
-  silc_protocol_execute(protocol, server->schedule, 0, 0);
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
 }
   
 /* This function connects to our primary router or if we are a router this
@@ -606,64 +631,45 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
+  SilcServerConfigSectionServerConnection *ptr;
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
-  /* If we are normal SILC server we need to connect to our cell's
-     router. */
   if (server->server_type == SILC_SERVER) {
     SILC_LOG_DEBUG(("We are normal server"));
+  } else if (server->server_type == SILC_ROUTER) {
+    SILC_LOG_DEBUG(("We are router"));
+  } else {
+    SILC_LOG_DEBUG(("We are backup router/normal server"));
+  }
 
-    /* Create connection to the router, if configured. */
-    if (server->config->routers) {
+  /* Create the connections to all our routes */
+  ptr = server->config->routers;
+  while (ptr) {
+    
+    SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+                   ptr->backup_router ? "Backup router" : "Router",
+                   ptr->initiator ? "Initiator" : "Responder",
+                   ptr->host, ptr->port));
 
+    if (ptr->initiator) {
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
-      sconn->remote_host = strdup(server->config->routers->host);
-      sconn->remote_port = server->config->routers->port;
-
+      sconn->remote_host = strdup(ptr->host);
+      sconn->remote_port = ptr->port;
+      sconn->backup = ptr->backup_router;
+      
       silc_schedule_task_add(server->schedule, fd, 
-                        silc_server_connect_router,
-                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                        SILC_TASK_PRI_NORMAL);
-      return;
+                            silc_server_connect_router,
+                            (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                            SILC_TASK_PRI_NORMAL);
     }
-  }
-
-  /* If we are a SILC router we need to establish all of our primary
-     routes. */
-  if (server->server_type == SILC_ROUTER) {
-    SilcServerConfigSectionServerConnection *ptr;
 
-    SILC_LOG_DEBUG(("We are router"));
-
-    /* Create the connections to all our routes */
-    ptr = server->config->routers;
-    while (ptr) {
-
-      SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
-                     ptr->initiator ? "Initiator" : "Responder",
-                     ptr->host, ptr->port));
-
-      if (ptr->initiator) {
-       /* Allocate connection object for hold connection specific stuff. */
-       sconn = silc_calloc(1, sizeof(*sconn));
-       sconn->server = server;
-       sconn->remote_host = strdup(ptr->host);
-       sconn->remote_port = ptr->port;
-
-       silc_schedule_task_add(server->schedule, fd, 
-                          silc_server_connect_router,
-                          (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
-                          SILC_TASK_PRI_NORMAL);
-      }
-
-      if (!ptr->next)
-       return;
-
-      ptr = ptr->next;
-    }
+    if (!ptr->next)
+      return;
+    
+    ptr = ptr->next;
   }
 
   SILC_LOG_DEBUG(("No router(s), server will be standalone"));
@@ -699,11 +705,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -726,11 +731,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -768,8 +772,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
@@ -830,8 +833,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
@@ -839,7 +841,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add a task to the queue. This task receives new connections to the 
      server. This task remains on the queue until the end of the program. */
-  if (!server->listenning) {
+  if (!server->listenning && !sconn->backup) {
     silc_schedule_task_add(server->schedule, server->sock, 
                           silc_server_accept_new_connection,
                           (void *)server, 0, 0, 
@@ -871,12 +873,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SILC_LOG_INFO(("Connected to router %s", sock->hostname));
 
   /* Add the connected router to local server list */
-  server->standalone = FALSE;
   id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname),
                                    SILC_ROUTER, ctx->dest_id, NULL, sock);
   if (!id_entry) {
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     goto out;
@@ -886,8 +886,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   silc_free(sock->user_data);
   sock->user_data = (void *)id_entry;
   sock->type = SILC_SOCKET_TYPE_ROUTER;
-  server->id_entry->router = id_entry;
-  server->router = id_entry;
   idata = (SilcIDListData)sock->user_data;
   idata->status |= SILC_IDLIST_STATUS_REGISTERED;
 
@@ -904,17 +902,34 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   idata->rekey->timeout = 3600; /* XXX hardcoded */
   idata->rekey->context = (void *)server;
   silc_schedule_task_add(server->schedule, sock->sock, 
-                    silc_server_rekey_callback,
-                    (void *)sock, idata->rekey->timeout, 0,
-                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+                        silc_server_rekey_callback,
+                        (void *)sock, idata->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
-  /* If we are router then announce our possible servers. */
-  if (server->server_type == SILC_ROUTER)
-    silc_server_announce_servers(server);
+  if (!sconn->backup) {
+    /* Mark this router our primary router if we're still standalone */
+    if (server->standalone) {
+      server->id_entry->router = id_entry;
+      server->router = id_entry;
+      server->standalone = FALSE;
+    }
+    
+    /* If we are router then announce our possible servers. */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_announce_servers(server, FALSE, 0);
+
+    /* Announce our clients and channels to the router */
+    silc_server_announce_clients(server, 0);
+    silc_server_announce_channels(server, 0);
+  } else {
+    /* Add this server to be our backup router */
+    silc_server_backup_add(server, id_entry, FALSE);
+  }
 
-  /* Announce our clients and channels to the router */
-  silc_server_announce_clients(server);
-  silc_server_announce_channels(server);
+  /* Call the completion callback to indicate that we've connected to
+     the router */
+  if (sconn->callback)
+    (*sconn->callback)(server, id_entry, sconn->callback_context);
 
  out:
   /* Free the temporary connection data context */
@@ -1124,11 +1139,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
@@ -1152,11 +1166,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
@@ -1229,13 +1242,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
     if (sock)
       sock->protocol = NULL;
     silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     server->stat.auth_failures++;
@@ -1287,10 +1299,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       SILC_LOG_DEBUG(("Remote host is %s", 
                      ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
-                     "server" : "router"));
+                     "server" : (conn->backup_router ? 
+                                 "backup router" : "router")));
       SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
                     sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
-                    "server" : "router"));
+                    "server" : (conn->backup_router ? 
+                                "backup router" : "router")));
 
       /* Add the server into server cache. The server name and Server ID
         is updated after we have received NEW_SERVER packet from the
@@ -1321,8 +1335,28 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
       id_entry = (void *)new_server;
 
+      /* If the incoming connection is router and marked as backup router
+        then add it to be one of our backups */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && conn->backup_router) {
+       silc_server_backup_add(server, new_server, conn->backup_local);
+
+       /* Change it back to SERVER type since that's what it really is. */
+       if (conn->backup_local) {
+         ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
+         new_server->server_type = SILC_SOCKET_TYPE_SERVER;
+       }
+      }
+
+#if 0
+      /* If the incoming connection is normal server and marked as backup
+        server then it will use us as backup router. We'll disable the
+        connection until it is allowed to be used. */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER && conn->backup_router)
+       SILC_SET_DISABLED(sock);
+#endif
+
       /* Check whether this connection is to be our primary router connection
-        if we dont' already have the primary route. */
+        if we do not already have the primary route. */
       if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
        if (silc_server_config_is_primary_route(server->config) &&
            !conn->initiator)
@@ -1372,8 +1406,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
-  if (ctx->dest_id)
-    silc_free(ctx->dest_id);
+  silc_free(ctx->dest_id);
   silc_free(ctx);
   sock->protocol = NULL;
 }
@@ -1468,16 +1501,6 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
     SILC_SET_DISCONNECTING(sock);
 
-    /* If the closed connection was our primary router connection the
-       start re-connecting phase. */
-    if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && 
-       sock == server->router->connection)
-      silc_schedule_task_add(server->schedule, 0, 
-                            silc_server_connect_to_router,
-                            context, 1, 0,
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
-
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock);
     silc_server_close_connection(server, sock);
@@ -1568,6 +1591,10 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     goto out;
   }
 
+  /* If entry is disabled ignore what we got. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    goto out;
+
   if (ret == 0) {
     /* Parse the packet. Packet type is returned. */
     ret = silc_packet_parse(packet);
@@ -1625,12 +1652,16 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
        packet->flags & SILC_PACKET_FLAG_BROADCAST &&
        !server->standalone) {
+      /* Broadcast to our primary route */
       silc_server_packet_broadcast(server, server->router->connection, packet);
+
+      /* If we have backup routers then we need to feed all broadcast
+        data to those servers. */
+      silc_server_backup_broadcast(server, sock, packet);
     }
   }
 
  out:
-  /*  silc_buffer_clear(sock->inbuf); */
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
@@ -2078,6 +2109,19 @@ void silc_server_packet_parse_type(SilcServer server,
     }
     break;
 
+  case SILC_PACKET_FTP:
+    /* Ignored */
+    break;
+
+  case SILC_PACKET_RESUME_ROUTER:
+    /* Resume router packet received. This packet is received for backup
+       router resuming protocol. */
+    SILC_LOG_DEBUG(("Resume router packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_backup_resume_router(server, sock, packet);
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -2208,19 +2252,7 @@ void silc_server_free_client_data(SilcServer server,
 
   /* If there is pending outgoing data for the client then purge it
      to the network before removing the client entry. */
-  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
-      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
-    server->stat.packets_sent++;
-
-    if (sock->outbuf->data - sock->outbuf->head)
-     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
-
-    silc_packet_send(sock, TRUE);
-
-    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-    silc_buffer_clear(sock->outbuf);
-  }
+  silc_server_packet_queue_purge(server, sock);
 
   /* Send SIGNOFF notify to routers. */
   if (notify && !server->standalone && server->router)
@@ -2275,27 +2307,83 @@ void silc_server_free_sock_user_data(SilcServer server,
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
-
-      /* Free all client entries that this server owns as they will
-        become invalid now as well. */
-      if (user_data->id)
-       silc_server_remove_clients_by_server(server, user_data, TRUE);
+      SilcServerEntry backup_router = NULL;
 
       /* If this was our primary router connection then we're lost to
         the outside world. */
       if (server->router == user_data) {
-       server->id_entry->router = NULL;
-       server->router = NULL;
-       server->standalone = TRUE;
+       backup_router = silc_server_backup_get(server);
+
+       /* Check whether we have a backup router connection */
+       if (!backup_router || backup_router == user_data) {
+         silc_schedule_task_add(server->schedule, 0, 
+                                silc_server_connect_to_router,
+                                server, 1, 0,
+                                SILC_TASK_TIMEOUT,
+                                SILC_TASK_PRI_NORMAL);
+
+         server->id_entry->router = NULL;
+         server->router = NULL;
+         server->standalone = TRUE;
+         backup_router = NULL;
+       } else {
+         SILC_LOG_INFO(("New primary router is backup router %s",
+                        backup_router->server_name));
+         SILC_LOG_DEBUG(("New primary router is backup router %s",
+                         backup_router->server_name));
+         server->id_entry->router = backup_router;
+         server->router = backup_router;
+         server->router_connect = time(0);
+         if (server->server_type == SILC_BACKUP_ROUTER) {
+           server->server_type = SILC_ROUTER;
+
+           /* We'll need to constantly try to reconnect to the primary
+              router so that we'll see when it comes back online. */
+           silc_server_backup_reconnect(server, sock->ip, sock->port,
+                                        silc_server_backup_connected,
+                                        NULL);
+         }
+
+         /* Mark this connection as replaced */
+         silc_server_backup_replaced_add(server, user_data->id, 
+                                         backup_router);
+       }
+      }
+
+      if (!backup_router) {
+       /* Free all client entries that this server owns as they will
+          become invalid now as well. */
+       if (user_data->id)
+         silc_server_remove_clients_by_server(server, user_data, TRUE);
+      } else {
+       /* Update the client entries of this server to the new backup
+          router. This also removes the clients that *really* was owned
+          by the primary router and went down with the router.  */
+       silc_server_update_clients_by_server(server, user_data, backup_router,
+                                            TRUE);
       }
 
       /* Free the server entry */
+      silc_server_backup_del(server, user_data);
+      if (user_data->id)
+       silc_server_backup_replaced_del(server, user_data->id);
       silc_idlist_del_data(user_data);
       silc_idlist_del_server(server->local_list, user_data);
       server->stat.my_servers--;
       server->stat.servers--;
       if (server->server_type == SILC_ROUTER)
        server->stat.cell_servers--;
+
+      if (backup_router) {
+       /* Announce all of our stuff that was created about 5 minutes ago.
+          The backup router knows all the other stuff already. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server, FALSE, time(0) - 300);
+
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, time(0) - 300);
+       silc_server_announce_channels(server, time(0) - 300);
+      }
       break;
     }
   default:
@@ -2319,346 +2407,6 @@ void silc_server_free_sock_user_data(SilcServer server,
   sock->user_data = NULL;
 }
 
-/* Removes the client from channels and possibly removes the channels
-   as well.  After removing those channels that exist, their channel
-   keys are regnerated. This is called only by the function
-   silc_server_remove_clients_by_server. */
-
-static void silc_server_remove_clients_channels(SilcServer server, 
-                                               SilcSocketConnection sock,
-                                               SilcClientEntry client,
-                                               SilcHashTable channels)
-{
-  SilcChannelEntry channel;
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-  SilcBuffer clidp;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (!client || !client->id)
-    return;
-
-  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
-  /* Remove the client from all channels. The client is removed from
-     the channels' user list. */
-  silc_hash_table_list(client->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    channel = chl->channel;
-
-    /* Remove channel from client's channel list */
-    silc_hash_table_del(client->channels, channel);
-
-    /* Remove channel if there is no users anymore */
-    if (server->server_type == SILC_ROUTER &&
-       silc_hash_table_count(channel->user_list) < 2) {
-
-      if (silc_hash_table_find(channels, channel, NULL, NULL))
-       silc_hash_table_del(channels, channel);
-
-      if (channel->rekey)
-       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
-      continue;
-    }
-
-    /* Remove client from channel's client list */
-    silc_hash_table_del(channel->user_list, chl->client);
-    silc_free(chl);
-    server->stat.my_chanclients--;
-
-    /* If there is no global users on the channel anymore mark the channel
-       as local channel. Do not check if the removed client is local client. */
-    if (server->server_type == SILC_SERVER && channel->global_users && 
-       chl->client->router && !silc_server_channel_has_global(channel))
-      channel->global_users = FALSE;
-
-    /* If there is not at least one local user on the channel then we don't
-       need the channel entry anymore, we can remove it safely. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_local(channel)) {
-
-      if (silc_hash_table_find(channels, channel, NULL, NULL))
-       silc_hash_table_del(channels, channel);
-
-      if (channel->rekey)
-       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
-      if (channel->founder_key) {
-       /* The founder auth data exists, do not remove the channel entry */
-       SilcChannelClientEntry chl2;
-       SilcHashTableList htl2;
-
-       channel->id = NULL;
-
-       silc_hash_table_list(channel->user_list, &htl2);
-       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
-         silc_hash_table_del(chl2->client->channels, channel);
-         silc_hash_table_del(channel->user_list, chl2->client);
-         silc_free(chl2);
-       }
-       continue;
-      }
-
-      /* Remove the channel entry */
-      if (!silc_idlist_del_channel(server->local_list, channel))
-       silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
-      continue;
-    }
-
-    /* Add the channel to the the channels list to regenerate the 
-       channel key */
-    if (!silc_hash_table_find(channels, channel, NULL, NULL))
-      silc_hash_table_add(channels, channel, channel);
-  }
-
-  silc_buffer_free(clidp);
-}
-
-/* This function is used to remove all client entries by the server `entry'.
-   This is called when the connection is lost to the server. In this case
-   we must invalidate all the client entries owned by the server `entry'. 
-   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
-   distributed to our local clients. */
-
-int silc_server_remove_clients_by_server(SilcServer server, 
-                                        SilcServerEntry entry,
-                                        int server_signoff)
-{
-  SilcIDCacheList list = NULL;
-  SilcIDCacheEntry id_cache = NULL;
-  SilcClientEntry client = NULL;
-  SilcBuffer idp;
-  SilcClientEntry *clients = NULL;
-  uint32 clients_c = 0;
-  unsigned char **argv = NULL;
-  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
-  SilcHashTableList htl;
-  SilcChannelEntry channel;
-  SilcHashTable channels;
-  int i;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Allocate the hash table that holds the channels that require
-     channel key re-generation after we've removed this server's clients
-     from the channels. */
-  channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
-                                  NULL, NULL, TRUE);
-
-  if (server_signoff) {
-    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
-    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
-    argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-    memcpy(argv[argc], idp->data, idp->len);
-    argv_lens[argc] = idp->len;
-    argv_types[argc] = argc + 1;
-    argc++;
-    silc_buffer_free(idp);
-  }
-
-  if (silc_idcache_get_all(server->local_list->clients, &list)) {
-
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
-         }
-
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
-       }
-
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->local_list, client);
-
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
-      }
-    }
-    silc_idcache_list_free(list);
-  }
-  
-  if (silc_idcache_get_all(server->global_list->clients, &list)) {
-
-    if (silc_idcache_list_first(list, &id_cache)) {
-      while (id_cache) {
-       client = (SilcClientEntry)id_cache->context;
-       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-       
-       if (client->router != entry) {
-         if (server_signoff && client->connection) {
-           clients = silc_realloc(clients, 
-                                  sizeof(*clients) * (clients_c + 1));
-           clients[clients_c] = client;
-           clients_c++;
-         }
-
-         if (!silc_idcache_list_next(list, &id_cache))
-           break;
-         else
-           continue;
-       }
-
-       if (server_signoff) {
-         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
-         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
-                                  (argc + 1));
-         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
-                                   (argc + 1));
-         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
-         memcpy(argv[argc], idp->data, idp->len);
-         argv_lens[argc] = idp->len;
-         argv_types[argc] = argc + 1;
-         argc++;
-         silc_buffer_free(idp);
-       }
-
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       silc_idlist_del_client(server->global_list, client);
-
-       if (!silc_idcache_list_next(list, &id_cache))
-         break;
-      }
-    }
-    silc_idcache_list_free(list);
-  }
-
-  /* Send the SERVER_SIGNOFF notify */
-  if (server_signoff) {
-    SilcBuffer args;
-
-    /* Send SERVER_SIGNOFF notify to our primary router */
-    if (!server->standalone && server->router &&
-       server->router != entry) {
-      args = silc_argument_payload_encode(1, argv, argv_lens,
-                                         argv_types);
-      silc_server_send_notify_args(server, 
-                                  server->router->connection,
-                                  server->server_type == SILC_SERVER ? 
-                                  FALSE : TRUE, 
-                                  SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
-      silc_buffer_free(args);
-    }
-
-    args = silc_argument_payload_encode(argc, argv, argv_lens,
-                                       argv_types);
-    /* Send to local clients */
-    for (i = 0; i < clients_c; i++) {
-      silc_server_send_notify_args(server, clients[i]->connection,
-                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
-                                  argc, args);
-    }
-
-    silc_free(clients);
-    silc_buffer_free(args);
-    for (i = 0; i < argc; i++)
-      silc_free(argv[i]);
-    silc_free(argv);
-    silc_free(argv_lens);
-    silc_free(argv_types);
-  }
-
-  /* We must now re-generate the channel key for all channels that had
-     this server's client(s) on the channel. As they left the channel we
-     must re-generate the channel key. */
-  silc_hash_table_list(channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
-    if (!silc_server_create_channel_key(server, channel, 0))
-      return FALSE;
-
-    /* Do not send the channel key if private channel key mode is set */
-    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
-      continue;
-
-    silc_server_send_channel_key(server, NULL, channel, 
-                                server->server_type == SILC_ROUTER ? 
-                                FALSE : !server->standalone);
-  }
-  silc_hash_table_free(channels);
-
-  return TRUE;
-}
-
-/* Checks whether given channel has global users.  If it does this returns
-   TRUE and FALSE if there is only locally connected clients on the channel. */
-
-int silc_server_channel_has_global(SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-
-  silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (chl->client->router)
-      return TRUE;
-  }
-
-  return FALSE;
-}
-
-/* Checks whether given channel has locally connected users.  If it does this
-   returns TRUE and FALSE if there is not one locally connected client. */
-
-int silc_server_channel_has_local(SilcChannelEntry channel)
-{
-  SilcChannelClientEntry chl;
-  SilcHashTableList htl;
-
-  silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (!chl->client->router)
-      return TRUE;
-  }
-
-  return FALSE;
-}
-
 /* Removes client from all channels it has joined. This is used when client
    connection is disconnected. If the client on a channel is last, the
    channel is removed as well. This sends the SIGNOFF notify types. */
@@ -2704,18 +2452,19 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* Remove client from channel's client list */
     silc_hash_table_del(channel->user_list, chl->client);
-    silc_free(chl);
-    server->stat.my_chanclients--;
 
     /* If there is no global users on the channel anymore mark the channel
        as local channel. Do not check if the removed client is local client. */
-    if (server->server_type == SILC_SERVER && channel->global_users && 
+    if (server->server_type != SILC_ROUTER && channel->global_users && 
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
+    silc_free(chl);
+    server->stat.my_chanclients--;
+
     /* If there is not at least one local user on the channel then we don't
        need the channel entry anymore, we can remove it safely. */
-    if (server->server_type == SILC_SERVER &&
+    if (server->server_type != SILC_ROUTER &&
        !silc_server_channel_has_local(channel)) {
       /* Notify about leaving client if this channel has global users. */
       if (notify && channel->global_users)
@@ -2822,18 +2571,19 @@ int silc_server_remove_from_one_channel(SilcServer server,
 
   /* Remove client from channel's client list */
   silc_hash_table_del(channel->user_list, chl->client);
-  silc_free(chl);
-  server->stat.my_chanclients--;
   
   /* If there is no global users on the channel anymore mark the channel
      as local channel. Do not check if the client is local client. */
-  if (server->server_type == SILC_SERVER && channel->global_users &&
+  if (server->server_type != SILC_ROUTER && channel->global_users &&
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
+  silc_free(chl);
+  server->stat.my_chanclients--;
+
   /* If there is not at least one local user on the channel then we don't
      need the channel entry anymore, we can remove it safely. */
-  if (server->server_type == SILC_SERVER &&
+  if (server->server_type != SILC_ROUTER &&
       !silc_server_channel_has_local(channel)) {
     /* Notify about leaving client if this channel has global users. */
     if (notify && channel->global_users)
@@ -2879,23 +2629,6 @@ int silc_server_remove_from_one_channel(SilcServer server,
   return TRUE;
 }
 
-/* Returns TRUE if the given client is on the channel.  FALSE if not. 
-   This works because we assure that the user list on the channel is
-   always in up to date thus we can only check the channel list from 
-   `client' which is faster than checking the user list from `channel'. */
-
-int silc_server_client_on_channel(SilcClientEntry client,
-                                 SilcChannelEntry channel)
-{
-  if (!client || !channel)
-    return FALSE;
-
-  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
-    return TRUE;
-
-  return FALSE;
-}
-
 /* Timeout callback. This is called if connection is idle or for some
    other reason is not responding within some period of time. This 
    disconnects the remote end. */
@@ -3293,7 +3026,8 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock,
 static void silc_server_announce_get_servers(SilcServer server,
                                             SilcServerEntry remote,
                                             SilcIDList id_list,
-                                            SilcBuffer *servers)
+                                            SilcBuffer *servers,
+                                            unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3307,8 +3041,10 @@ static void silc_server_announce_get_servers(SilcServer server,
        entry = (SilcServerEntry)id_cache->context;
 
        /* Do not announce the one we've sending our announcements and
-          do not announce ourself. */
-       if (entry == remote || entry == server->id_entry) {
+          do not announce ourself. Also check the creation time if it's
+          provided. */
+       if ((entry == remote) || (entry == server->id_entry) ||
+           (creation_time && entry->data.created < creation_time)) {
          if (!silc_idcache_list_next(list, &id_cache))
            break;
          continue;
@@ -3335,9 +3071,12 @@ static void silc_server_announce_get_servers(SilcServer server,
 }
 
 /* This function is used by router to announce existing servers to our
-   primary router when we've connected to it. */
+   primary router when we've connected to it. If `creation_time' is non-zero
+   then only the servers that has been created after the `creation_time'
+   will be announced. */
 
-void silc_server_announce_servers(SilcServer server)
+void silc_server_announce_servers(SilcServer server, bool global,
+                                 unsigned long creation_time)
 {
   SilcBuffer servers = NULL;
 
@@ -3345,11 +3084,14 @@ void silc_server_announce_servers(SilcServer server)
 
   /* Get servers in local list */
   silc_server_announce_get_servers(server, server->router,
-                                  server->local_list, &servers);
+                                  server->local_list, &servers,
+                                  creation_time);
 
-  /* Get servers in global list */
-  silc_server_announce_get_servers(server, server->router,
-                                  server->global_list, &servers);
+  if (global)
+    /* Get servers in global list */
+    silc_server_announce_get_servers(server, server->router,
+                                    server->global_list, &servers,
+                                    creation_time);
 
   if (servers) {
     silc_buffer_push(servers, servers->data - servers->head);
@@ -3369,7 +3111,8 @@ void silc_server_announce_servers(SilcServer server)
 
 static void silc_server_announce_get_clients(SilcServer server,
                                             SilcIDList id_list,
-                                            SilcBuffer *clients)
+                                            SilcBuffer *clients,
+                                            unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3382,6 +3125,12 @@ static void silc_server_announce_get_clients(SilcServer server,
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
 
+       if (creation_time && client->data.created < creation_time) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
+
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
        *clients = silc_buffer_realloc(*clients, 
@@ -3403,9 +3152,12 @@ static void silc_server_announce_get_clients(SilcServer server,
 }
 
 /* This function is used to announce our existing clients to our router
-   when we've connected to it. */
+   when we've connected to it. If `creation_time' is non-zero then only
+   the clients that has been created after the `creation_time' will be
+   announced. */
 
-void silc_server_announce_clients(SilcServer server)
+void silc_server_announce_clients(SilcServer server,
+                                 unsigned long creation_time)
 {
   SilcBuffer clients = NULL;
 
@@ -3413,12 +3165,12 @@ void silc_server_announce_clients(SilcServer server)
 
   /* Get clients in local list */
   silc_server_announce_get_clients(server, server->local_list,
-                                  &clients);
+                                  &clients, creation_time);
 
   /* As router we announce our global list as well */
   if (server->server_type == SILC_ROUTER)
     silc_server_announce_get_clients(server, server->global_list,
-                                    &clients);
+                                    &clients, creation_time);
 
   if (clients) {
     silc_buffer_push(clients, clients->data - clients->head);
@@ -3519,7 +3271,8 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
                                       uint32 *channel_users_modes_c,
-                                      SilcChannelID ***channel_ids)
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
@@ -3529,6 +3282,7 @@ void silc_server_announce_get_channels(SilcServer server,
   uint16 name_len;
   int len;
   int i = *channel_users_modes_c;
+  bool announce;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -3537,26 +3291,34 @@ void silc_server_announce_get_channels(SilcServer server,
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        channel = (SilcChannelEntry)id_cache->context;
-       
+
+       if (creation_time && channel->created < creation_time)
+         announce = FALSE;
+       else
+         announce = TRUE;
+
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
-       len = 4 + name_len + id_len + 4;
-       *channels = 
-         silc_buffer_realloc(*channels, 
-                             (*channels ? (*channels)->truelen + len : len));
-       silc_buffer_pull_tail(*channels, 
-                             ((*channels)->end - (*channels)->data));
-       silc_buffer_format(*channels,
-                          SILC_STR_UI_SHORT(name_len),
-                          SILC_STR_UI_XNSTRING(channel->channel_name, 
-                                               name_len),
-                          SILC_STR_UI_SHORT(id_len),
-                          SILC_STR_UI_XNSTRING(cid, id_len),
-                          SILC_STR_UI_INT(channel->mode),
-                          SILC_STR_END);
-       silc_buffer_pull(*channels, len);
+       if (announce) {
+         len = 4 + name_len + id_len + 4;
+         *channels = 
+           silc_buffer_realloc(*channels, 
+                               (*channels ? (*channels)->truelen + 
+                                len : len));
+         silc_buffer_pull_tail(*channels, 
+                               ((*channels)->end - (*channels)->data));
+         silc_buffer_format(*channels,
+                            SILC_STR_UI_SHORT(name_len),
+                            SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                                 name_len),
+                            SILC_STR_UI_SHORT(id_len),
+                            SILC_STR_UI_XNSTRING(cid, id_len),
+                            SILC_STR_UI_INT(channel->mode),
+                            SILC_STR_END);
+         silc_buffer_pull(*channels, len);
+       }
 
        *channel_users_modes = silc_realloc(*channel_users_modes,
                                            sizeof(**channel_users_modes) * 
@@ -3584,9 +3346,13 @@ void silc_server_announce_get_channels(SilcServer server,
 
 /* This function is used to announce our existing channels to our router
    when we've connected to it. This also announces the users on the
-   channels to the router. */
+   channels to the router. If the `creation_time' is non-zero only the
+   channels that was created after the `creation_time' are announced.
+   Note that the channel users are still announced even if the `creation_time'
+   was provided. */
 
-void silc_server_announce_channels(SilcServer server)
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time)
 {
   SilcBuffer channels = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
@@ -3600,14 +3366,14 @@ void silc_server_announce_channels(SilcServer server)
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
-                                   &channel_ids);
+                                   &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
   silc_server_announce_get_channels(server, server->global_list,
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
-                                   &channel_ids);
+                                   &channel_ids, creation_time);
 
   if (channels) {
     silc_buffer_push(channels, channels->data - channels->head);
@@ -3857,7 +3623,7 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
 
   /* Destination belongs to someone not in this server. If we are normal
      server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
+  if (server->server_type != SILC_ROUTER && !server->standalone) {
     silc_free(id);
     if (idata)
       *idata = (SilcIDListData)server->router;
@@ -4031,6 +3797,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
        protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
+    SILC_LOG_ERROR(("Error occurred during rekey protocol"));
     silc_protocol_cancel(protocol, server->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
@@ -4042,6 +3809,10 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     return;
   }
 
+  /* Purge the outgoing data queue to assure that all rekey packets really
+     go to the network before we quit the protocol. */
+  silc_server_packet_queue_purge(server, sock);
+
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;