Fixed server/backup router reconnecting
[silc.git] / apps / silcd / server.c
index 16d2298881a375e8e088cd4ed047529175e3df1e..bb5b3b279e819a72d240fa388f5b8fb7610d0e0a 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2005, 2007 Pekka Riikonen
+  Copyright (C) 1997 - 2008 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
@@ -24,6 +24,7 @@
 
 SILC_TASK_CALLBACK(silc_server_get_stats);
 SILC_TASK_CALLBACK(silc_server_connect_router);
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry);
 SILC_TASK_CALLBACK(silc_server_do_rekey);
 SILC_TASK_CALLBACK(silc_server_purge_expired_clients);
 static void silc_server_accept_new_connection(SilcNetStatus status,
@@ -79,6 +80,9 @@ static SilcBool silc_server_packet_receive(SilcPacketEngine engine,
   SilcServer server = callback_context;
   SilcIDListData idata = stream_context;
 
+  if (!idata)
+    return FALSE;
+
   /* Packets we do not handle */
   switch (packet->type) {
   case SILC_PACKET_HEARTBEAT:
@@ -99,12 +103,13 @@ static SilcBool silc_server_packet_receive(SilcPacketEngine engine,
        !(idata->status & SILC_IDLIST_STATUS_REGISTERED)) &&
       packet->type != SILC_PACKET_NEW_CLIENT &&
       packet->type != SILC_PACKET_NEW_SERVER &&
+      packet->type != SILC_PACKET_RESUME_CLIENT &&
       packet->type != SILC_PACKET_CONNECTION_AUTH_REQUEST &&
       packet->type != SILC_PACKET_DISCONNECT)
     return FALSE;
 
-  /* NEW_CLIENT and NEW_SERVER are accepted only without source ID
-     and for unregistered connection. */
+  /* NEW_CLIENT and NEW_SERVER are accepted only without source ID and
+     for unregistered connection. */
   if (packet->src_id && (packet->type == SILC_PACKET_NEW_CLIENT ||
                         packet->type == SILC_PACKET_NEW_SERVER) &&
       (idata->status & SILC_IDLIST_STATUS_REGISTERED))
@@ -126,7 +131,8 @@ static SilcBool silc_server_packet_receive(SilcPacketEngine engine,
        silc_id_str2id(packet->src_id, packet->src_id_len,
                       packet->src_id_type, &client_id, sizeof(client_id))) {
       if (!SILC_ID_CLIENT_COMPARE(client->id, &client_id)) {
-       SILC_LOG_DEBUG(("Packet source is not same as sender"));
+       SILC_LOG_DEBUG(("Packet source is not same as sender, packet %s",
+                       silc_get_packet_name(packet->type)));
        return FALSE;
       }
     }
@@ -188,7 +194,35 @@ static void silc_server_packet_eos(SilcPacketEngine engine,
   SilcServer server = callback_context;
   SilcIDListData idata = silc_packet_get_context(stream);
 
-  SILC_LOG_DEBUG(("End of stream received"));
+  SILC_LOG_DEBUG(("End of stream received, sock %p", stream));
+
+  if (!idata)
+    return;
+
+  if (server->router_conn && server->router_conn->sock == stream &&
+      !server->router && server->standalone) {
+    silc_server_create_connections(server);
+    silc_server_free_sock_user_data(server, stream, NULL);
+  } else {
+    /* If backup disconnected then mark that resuming will not be allowed */
+     if (server->server_type == SILC_ROUTER && !server->backup_router &&
+         idata->conn_type == SILC_CONN_SERVER) {
+      SilcServerEntry server_entry = (SilcServerEntry)idata;
+      if (server_entry->server_type == SILC_BACKUP_ROUTER)
+        server->backup_closed = TRUE;
+    }
+
+    silc_server_free_sock_user_data(server, stream, NULL);
+  }
+
+  silc_server_close_connection(server, stream);
+}
+
+SILC_TASK_CALLBACK(silc_server_packet_error_timeout)
+{
+  SilcServer server = app_context;
+  SilcPacketStream stream = context;
+  SilcIDListData idata = silc_packet_get_context(stream);
 
   if (!idata)
     return;
@@ -219,20 +253,30 @@ static void silc_server_packet_error(SilcPacketEngine engine,
                                     void *callback_context,
                                     void *stream_context)
 {
+  SilcServer server = callback_context;
   SilcIDListData idata = silc_packet_get_context(stream);
   SilcStream sock = silc_packet_stream_get_stream(stream);
   const char *ip;
   SilcUInt16 port;
 
+  SILC_LOG_DEBUG(("Packet error, sock %p", stream));
+
   if (!idata || !sock)
     return;
 
   if (!silc_socket_stream_get_info(sock, NULL, NULL, &ip, &port))
     return;
 
-  SILC_LOG_ERROR(("Connection %s:%d [%s]: %s",
-                 SILC_CONNTYPE_STRING(idata->conn_type), ip, port,
+  SILC_LOG_ERROR(("Connection %s:%d [%s]: %s", ip, port,
+                 SILC_CONNTYPE_STRING(idata->conn_type),
                  silc_packet_error_string(error)));
+
+  if (!silc_packet_stream_is_valid(stream))
+    return;
+
+  silc_schedule_task_add_timeout(server->schedule,
+                                silc_server_packet_error_timeout,
+                                stream, 0, 0);
 }
 
 /* Packet stream callbacks */
@@ -337,11 +381,13 @@ static void silc_server_packet_parse_type(SilcServer server,
 
       status = (SilcStatus)packet->buffer.data[0];
       if (silc_buffer_len(&packet->buffer) > 1 &&
-         silc_utf8_valid(packet->buffer.data + 1, silc_buffer_len(&packet->buffer) - 1))
+         silc_utf8_valid(packet->buffer.data + 1,
+                         silc_buffer_len(&packet->buffer) - 1))
        message = silc_memdup(packet->buffer.data + 1,
                              silc_buffer_len(&packet->buffer) - 1);
 
-      if (!silc_socket_stream_get_info(sock, NULL, &hostname, &ip, NULL))
+      if (!silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                                      NULL, &hostname, &ip, NULL))
        break;
 
       SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", ip, hostname,
@@ -354,21 +400,18 @@ static void silc_server_packet_parse_type(SilcServer server,
       server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
 
       /* If backup disconnected then mark that resuming will not be allowed */
-#if 0
       if (server->server_type == SILC_ROUTER && !server->backup_router &&
-         sock->type == SILC_CONN_SERVER && sock->user_data) {
-       SilcServerEntry server_entry = sock->user_data;
+         idata->conn_type == SILC_CONN_SERVER) {
+       SilcServerEntry server_entry = (SilcServerEntry)idata;
        if (server_entry->server_type == SILC_BACKUP_ROUTER)
          server->backup_closed = TRUE;
       }
 
       /* Handle the disconnection from our end too */
-      if (sock->user_data && SILC_IS_LOCAL(sock->user_data))
+      if (SILC_IS_LOCAL(idata))
        silc_server_free_sock_user_data(server, sock, NULL);
-      SILC_SET_DISCONNECTING(sock);
       silc_server_close_connection(server, sock);
       server->backup_noswitch = FALSE;
-#endif
     }
     break;
 
@@ -497,9 +540,7 @@ static void silc_server_packet_parse_type(SilcServer server,
        router resuming protocol. */
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
-#if 0
     silc_server_backup_resume_router(server, sock, packet);
-#endif
     break;
 
   default:
@@ -560,6 +601,9 @@ void silc_server_free(SilcServer server)
 {
   SilcList list;
   SilcIDCacheEntry cache;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Free server %p", server));
 
   if (!server)
     return;
@@ -574,8 +618,12 @@ void silc_server_free(SilcServer server)
     silc_pkcs_private_key_free(server->private_key);
   if (server->pending_commands)
     silc_dlist_uninit(server->pending_commands);
-  if (server->id_entry)
+  if (server->id_entry) {
+    if (server->id_entry->data.sconn)
+      silc_schedule_task_del_by_context(server->schedule,
+                                       server->id_entry->data.sconn->sock);
     silc_idlist_del_server(server->local_list, server->id_entry);
+  }
 
   /* Delete all channels */
   if (silc_idcache_get_all(server->local_list->channels, &list)) {
@@ -592,26 +640,44 @@ void silc_server_free(SilcServer server)
   /* Delete all clients */
   if (silc_idcache_get_all(server->local_list->clients, &list)) {
     silc_list_start(list);
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      silc_schedule_task_del_by_context(server->schedule, cache->context);
       silc_idlist_del_client(server->local_list, cache->context);
+    }
   }
   if (silc_idcache_get_all(server->global_list->clients, &list)) {
     silc_list_start(list);
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      silc_schedule_task_del_by_context(server->schedule, cache->context);
       silc_idlist_del_client(server->global_list, cache->context);
+    }
   }
 
   /* Delete all servers */
   if (silc_idcache_get_all(server->local_list->servers, &list)) {
     silc_list_start(list);
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      idata = (SilcIDListData)cache->context;
+      if (idata->sconn)
+       silc_schedule_task_del_by_context(server->schedule,
+                                         idata->sconn->sock);
       silc_idlist_del_server(server->local_list, cache->context);
+    }
   }
   if (silc_idcache_get_all(server->global_list->servers, &list)) {
-    while ((cache = silc_list_get(list)))
+    while ((cache = silc_list_get(list))) {
+      idata = (SilcIDListData)cache->context;
+      if (idata->sconn)
+       silc_schedule_task_del_by_context(server->schedule,
+                                         idata->sconn->sock);
       silc_idlist_del_server(server->global_list, cache->context);
+    }
   }
 
+  silc_schedule_task_del_by_context(server->schedule, server);
+  silc_schedule_uninit(server->schedule);
+  server->schedule = NULL;
+
   silc_idcache_free(server->local_list->clients);
   silc_idcache_free(server->local_list->servers);
   silc_idcache_free(server->local_list->channels);
@@ -632,8 +698,6 @@ void silc_server_free(SilcServer server)
   silc_free(server->local_list);
   silc_free(server->global_list);
   silc_free(server->server_name);
-  silc_free(server->purge_i);
-  silc_free(server->purge_g);
   silc_free(server);
 
   silc_hmac_unregister_all();
@@ -667,60 +731,19 @@ silc_server_listen(SilcServer server, const char *server_ip, SilcUInt16 port)
 
 SilcBool silc_server_init_secondary(SilcServer server)
 {
-  return TRUE;
-#if 0
-  int sock = 0;
-  SilcPacketStream newsocket = NULL;
   SilcServerConfigServerInfoInterface *interface;
+  SilcNetListener listener;
 
   for (interface = server->config->server_info->secondary; interface;
-       interface = interface->next, sock++) {
-
-    if (!silc_server_listen(server,
-       interface->server_ip, interface->port, &sock_list[sock]))
-      goto err;
-
-    /* Set socket to non-blocking mode */
-    silc_net_set_socket_nonblock(sock_list[sock]);
-
-    /* Add ourselves also to the socket table. The entry allocated above
-       is sent as argument for fast referencing in the future. */
-    silc_socket_alloc(sock_list[sock],
-                     SILC_CONN_SERVER, NULL, &newsocket);
-    server->sockets[sock_list[sock]] = newsocket;
-    SILC_SET_LISTENER(newsocket);
-
-    /* Perform name and address lookups to resolve the listenning address
-       and port. */
-    if (!silc_net_check_local_by_sock(sock_list[sock], &newsocket->hostname,
-                           &newsocket->ip)) {
-      if ((server->config->require_reverse_lookup && !newsocket->hostname) ||
-        !newsocket->ip) {
-        SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
-                     newsocket->hostname ? newsocket->hostname :
-                     newsocket->ip ? newsocket->ip : ""));
-        server->stat.conn_failures++;
-        goto err;
-      }
-      if (!newsocket->hostname)
-        newsocket->hostname = strdup(newsocket->ip);
-    }
-    newsocket->port = silc_net_get_local_port(sock);
-
-    newsocket->user_data = (void *)server->id_entry;
-    silc_schedule_task_add(server->schedule, sock_list[sock],
-                          silc_server_accept_new_connection,
-                          (void *)server, 0, 0,
-                          SILC_TASK_FD,
-                          SILC_TASK_PRI_NORMAL);
+       interface = interface->next) {
+    listener = silc_server_listen(server, interface->server_ip,
+                                 interface->port);
+    if (!listener)
+      return FALSE;
+    silc_dlist_add(server->listeners, listener);
   }
 
   return TRUE;
-
- err:
-  do silc_net_close_server(sock_list[sock--]); while (sock >= 0);
-#endif /* 0 */
-  return FALSE;
 }
 
 /* Initializes the entire SILC server. This is called always before running
@@ -734,7 +757,6 @@ SilcBool silc_server_init(SilcServer server)
 {
   SilcServerID *id;
   SilcServerEntry id_entry;
-  SilcIDListPurge purge;
   SilcNetListener listener;
   SilcUInt16 *port;
   char **ip;
@@ -836,13 +858,14 @@ SilcBool silc_server_init(SilcServer server)
                   server->config->server_info->primary->port);
   if (!listener)
     goto err;
-
   silc_dlist_add(server->listeners, listener);
 
   /* Create a Server ID for the server. */
   port = silc_net_listener_get_port(listener, NULL);
   ip = silc_net_listener_get_ip(listener, NULL);
-  silc_id_create_server_id(ip[0], port[0], server->rng, &id);
+  silc_id_create_server_id(server->config->server_info->primary->public_ip ?
+                          server->config->server_info->primary->public_ip :
+                          ip[0], port[0], server->rng, &id);
   if (!id)
     goto err;
 
@@ -862,12 +885,16 @@ SilcBool silc_server_init(SilcServer server)
      the ID list. */
   id_entry =
     silc_idlist_add_server(server->local_list, strdup(server->server_name),
-                          server->server_type, server->id, NULL, NULL);
+                          server->server_type,
+                          silc_id_dup(server->id, SILC_ID_SERVER),
+                          NULL, NULL);
   if (!id_entry) {
     SILC_LOG_ERROR(("Could not add local server to cache"));
     goto err;
   }
   id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+  id_entry->data.conn_type = (server->server_type == SILC_SERVER ?
+                             SILC_CONN_SERVER : SILC_CONN_ROUTER);
   server->id_entry = id_entry;
 
   /* Create secondary TCP listeners */
@@ -896,22 +923,12 @@ SilcBool silc_server_init(SilcServer server)
     }
   }
 
-  /* Register the ID Cache purge task. This periodically purges the ID cache
-     and removes the expired cache entries. */
-
-  /* Clients local list */
-  server->purge_i = purge = silc_calloc(1, sizeof(*purge));
-  purge->cache = server->local_list->clients;
-  purge->timeout = 600;
-  silc_schedule_task_add_timeout(server->schedule, silc_idlist_purge,
-                                (void *)purge, purge->timeout, 0);
-
-  /* Clients global list */
-  server->purge_g = purge = silc_calloc(1, sizeof(*purge));
-  purge->cache = server->global_list->clients;
-  purge->timeout = 300;
-  silc_schedule_task_add_timeout(server->schedule, silc_idlist_purge,
-                                (void *)purge, purge->timeout, 0);
+  if (server->server_type != SILC_ROUTER) {
+    server->stat.servers = 1;
+    server->stat.cell_servers = 1;
+  } else {
+    server->stat.routers = 1;
+  }
 
   /* If we are normal server we'll retrieve network statisticial information
      once in a while from the router. */
@@ -919,9 +936,6 @@ SilcBool silc_server_init(SilcServer server)
     silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
                                   server, 10, 0);
 
-  if (server->server_type == SILC_ROUTER)
-    server->stat.routers++;
-
   /* Start packet engine */
   server->packet_engine =
     silc_packet_engine_start(server->rng, server->server_type == SILC_ROUTER,
@@ -932,7 +946,7 @@ SilcBool silc_server_init(SilcServer server)
   /* Register client entry expiration timeout */
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_purge_expired_clients, server,
-                                600, 0);
+                                120, 0);
 
   /* Initialize HTTP server */
   silc_server_http_init(server);
@@ -947,32 +961,28 @@ SilcBool silc_server_init(SilcServer server)
   return FALSE;
 }
 
-#if 0
 /* Task callback to close a socket connection after rehash */
 
 SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
 {
-  SilcServer server = context;
-  SilcPacketStream sock = server->sockets[fd];
+  SilcServer server = app_context;
+  SilcPacketStream sock = context;
+  SilcIDListData idata = silc_packet_get_context(sock);
+  const char *hostname;
+  SilcUInt16 port;
 
-  if (!sock)
-    return;
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                             NULL, &hostname, NULL, &port);
 
   SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
-                sock->hostname, sock->port,
-                (sock->type == SILC_CONN_UNKNOWN ? "Unknown" :
-                 sock->type == SILC_CONN_CLIENT ? "Client" :
-                 sock->type == SILC_CONN_SERVER ? "Server" :
-                 "Router")));
+                hostname, port, SILC_CONNTYPE_STRING(idata->conn_type)));
   silc_schedule_task_del_by_context(server->schedule, sock);
   silc_server_disconnect_remote(server, sock,
                                SILC_STATUS_ERR_BANNED_FROM_SERVER,
                                "This connection is removed from "
                                "configuration");
-  if (sock->user_data)
-    silc_server_free_sock_user_data(server, sock, NULL);
+  silc_server_free_sock_user_data(server, sock, NULL);
 }
-#endif /* 0 */
 
 /* This function basically reads the config file again and switches the config
    object pointed by the server object. After that, we have to fix various
@@ -981,7 +991,6 @@ SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
 
 SilcBool silc_server_rehash(SilcServer server)
 {
-#if 0
   SilcServerConfig newconfig;
 
   SILC_LOG_INFO(("Rehashing server"));
@@ -997,12 +1006,6 @@ SilcBool silc_server_rehash(SilcServer server)
     return FALSE;
   }
 
-  /* Reinit scheduler if necessary */
-  if (newconfig->param.connections_max > server->config->param.connections_max)
-    if (!silc_schedule_reinit(server->schedule,
-                             newconfig->param.connections_max))
-      return FALSE;
-
   /* Fix the server_name field */
   if (strcmp(server->server_name, newconfig->server_info->server_name)) {
     silc_free(server->server_name);
@@ -1021,13 +1024,10 @@ SilcBool silc_server_rehash(SilcServer server)
     /* Update the idcache list with a fresh pointer */
     silc_free(server->id_entry->server_name);
     server->id_entry->server_name = strdup(server->server_name);
-    if (!silc_idcache_del_by_context(server->local_list->servers,
-                                    server->id_entry))
-      return FALSE;
-    if (!silc_idcache_add(server->local_list->servers,
-                         strdup(server->id_entry->server_name),
-                         server->id_entry->id, server->id_entry, 0, NULL))
-      return FALSE;
+    silc_idcache_update_by_context(server->local_list->servers,
+                                  server->id_entry, NULL,
+                                  strdup(server->id_entry->server_name),
+                                  TRUE);
   }
 
   /* Set logging */
@@ -1043,13 +1043,6 @@ SilcBool silc_server_rehash(SilcServer server)
     server->private_key = newconfig->server_info->private_key;
     newconfig->server_info->public_key = NULL;
     newconfig->server_info->private_key = NULL;
-
-    /* Allocate PKCS context for local public and private keys */
-    silc_pkcs_free(server->pkcs);
-    if (!silc_pkcs_alloc(server->public_key->name, &server->pkcs))
-      return FALSE;
-    silc_pkcs_public_key_set(server->pkcs, server->public_key);
-    silc_pkcs_private_key_set(server->pkcs, server->private_key);
   }
 
   /* Check for unconfigured server and router connections and close
@@ -1078,11 +1071,10 @@ SilcBool silc_server_rehash(SilcServer server)
        SilcPacketStream sock;
        sock = silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
                                               ptr->host, ptr->port);
-       if (sock && !SILC_IS_LISTENER(sock))
-         silc_schedule_task_add(server->schedule, sock->sock,
-                                silc_server_rehash_close_connection,
-                                server, 0, 1, SILC_TASK_TIMEOUT,
-                                SILC_TASK_PRI_NORMAL);
+       if (sock)
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_rehash_close_connection,
+                                        sock, 0, 1);
       }
     }
   }
@@ -1108,11 +1100,10 @@ SilcBool silc_server_rehash(SilcServer server)
        SilcPacketStream sock;
        sock = silc_server_find_socket_by_host(server, SILC_CONN_SERVER,
                                               ptr->host, 0);
-       if (sock && !SILC_IS_LISTENER(sock))
-         silc_schedule_task_add(server->schedule, sock->sock,
-                                silc_server_rehash_close_connection,
-                                server, 0, 1, SILC_TASK_TIMEOUT,
-                                SILC_TASK_PRI_NORMAL);
+       if (sock)
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_rehash_close_connection,
+                                        sock, 0, 1);
       }
     }
   }
@@ -1139,10 +1130,9 @@ SilcBool silc_server_rehash(SilcServer server)
        sock = silc_server_find_socket_by_host(server, SILC_CONN_CLIENT,
                                               ptr->host, 0);
        if (sock)
-         silc_schedule_task_add(server->schedule, sock->sock,
-                                silc_server_rehash_close_connection,
-                                server, 0, 1, SILC_TASK_TIMEOUT,
-                                SILC_TASK_PRI_NORMAL);
+         silc_schedule_task_add_timeout(server->schedule,
+                                        silc_server_rehash_close_connection,
+                                        sock, 0, 1);
       }
     }
   }
@@ -1185,7 +1175,6 @@ SilcBool silc_server_rehash(SilcServer server)
 #endif /* SILC_DEBUG */
 
   SILC_LOG_DEBUG(("Server rehashed"));
-#endif /* 0 */
 
   return TRUE;
 }
@@ -1225,6 +1214,9 @@ void silc_server_stop(SilcServer server)
     while ((ps = silc_dlist_get(list))) {
       SilcIDListData idata = silc_packet_get_context(ps);
 
+      if (!silc_packet_stream_is_valid(ps))
+       continue;
+
       if (idata)
        idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
 
@@ -1233,7 +1225,7 @@ void silc_server_stop(SilcServer server)
       silc_server_free_sock_user_data(server, ps,
                                      "Server is shutting down");
     }
-    silc_dlist_uninit(list);
+    silc_packet_engine_free_streams_list(list);
   }
 
   /* We are not connected to network anymore */
@@ -1245,9 +1237,15 @@ void silc_server_stop(SilcServer server)
 
   silc_server_http_uninit(server);
 
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router_retry);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router);
+
   silc_schedule_stop(server->schedule);
-  silc_schedule_uninit(server->schedule);
-  server->schedule = NULL;
 
   SILC_LOG_DEBUG(("Server stopped"));
 }
@@ -1259,12 +1257,16 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
   SilcServer server = context;
   SilcClientEntry client;
   SilcIDList id_list;
+  SilcUInt64 curtime = silc_time();
 
   SILC_LOG_DEBUG(("Expire timeout"));
 
   silc_dlist_start(server->expired_clients);
   while ((client = silc_dlist_get(server->expired_clients))) {
-    if (client->data.status & SILC_IDLIST_STATUS_REGISTERED)
+    /* For unregistered clients the created timestamp is actually
+       unregistered timestamp.  Make sure client remains in history
+       at least 500 seconds. */
+    if (client->data.created && curtime - client->data.created < 500)
       continue;
 
     id_list = (client->data.status & SILC_IDLIST_STATUS_LOCAL ?
@@ -1277,7 +1279,7 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
 
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_purge_expired_clients, server,
-                                600, 0);
+                                120, 0);
 }
 
 
@@ -1285,8 +1287,11 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
 
 /* Free connection context */
 
-static void silc_server_connection_free(SilcServerConnection sconn)
+void silc_server_connection_free(SilcServerConnection sconn)
 {
+  if (!sconn)
+    return;
+  SILC_LOG_DEBUG(("Free connection %p", sconn));
   silc_dlist_del(sconn->server->conns, sconn);
   silc_server_config_unref(&sconn->conn);
   silc_free(sconn->remote_host);
@@ -1298,6 +1303,7 @@ static void silc_server_connection_free(SilcServerConnection sconn)
 
 void silc_server_create_connection(SilcServer server,
                                   SilcBool reconnect,
+                                  SilcBool dynamic,
                                   const char *remote_host, SilcUInt32 port,
                                   SilcServerConnectCallback callback,
                                   void *context)
@@ -1313,6 +1319,10 @@ void silc_server_create_connection(SilcServer server,
   sconn->no_reconnect = reconnect == FALSE;
   sconn->callback = callback;
   sconn->callback_context = context;
+  sconn->no_conf = dynamic;
+  sconn->server = server;
+
+  SILC_LOG_DEBUG(("Created connection %p", sconn));
 
   silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
                                 sconn, 0, 0);
@@ -1330,22 +1340,40 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
   SilcServerConfigServer *conn;
   SilcServerConfigConnParams *param;
   SilcIDListData idata;
-  SilcServerEntry id_entry;
+  SilcServerEntry id_entry = NULL;
   unsigned char id[32];
   SilcUInt32 id_len;
   SilcID remote_id;
+  const char *ip;
 
-  SILC_LOG_DEBUG(("Connection authentication completed"));
+  SILC_LOG_DEBUG(("Connection %p authentication completed, entry %p",
+                 sconn, entry));
+
+  entry->op = NULL;
 
   if (success == FALSE) {
     /* Authentication failed */
-    /* XXX retry connecting */
 
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_AUTH_FAILED, NULL);
     return;
   }
 
+  /* XXX For now remote is router always */
+  entry->data.conn_type = SILC_CONN_ROUTER;
+
   SILC_LOG_INFO(("Connected to %s %s",
                 SILC_CONNTYPE_STRING(entry->data.conn_type),
                 sconn->remote_host));
@@ -1361,13 +1389,20 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
                                      strdup(sconn->remote_host),
                                      SILC_SERVER, NULL, NULL, sconn->sock);
     if (!id_entry) {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_free_sock_user_data(server, sconn->sock, NULL);
       silc_server_disconnect_remote(server, sconn->sock,
                                    SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
-      silc_server_connection_free(sconn);
-      silc_free(entry);
       return;
     }
 
+    /* Statistics */
+    server->stat.my_servers++;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.servers++;
+    SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+
     silc_idlist_add_data(id_entry, (SilcIDListData)entry);
     break;
 
@@ -1383,10 +1418,11 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
                             SILC_STR_DATA(server->server_name,
                                           strlen(server->server_name)),
                             SILC_STR_END)) {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_free_sock_user_data(server, sconn->sock, NULL);
       silc_server_disconnect_remote(server, sconn->sock,
                                    SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
-      silc_server_connection_free(sconn);
-      silc_free(entry);
       return;
     }
 
@@ -1415,21 +1451,40 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
        as NULL since it's local to us. */
     id_entry = silc_idlist_add_server(server->global_list,
                                      strdup(sconn->remote_host),
-                                     SILC_ROUTER, &remote_id.u.server_id,
+                                     SILC_ROUTER,
+                                     silc_id_dup(&remote_id.u.server_id,
+                                                 SILC_ID_SERVER),
                                      NULL, sconn->sock);
     if (!id_entry) {
+      /* Try reconnecting if configuration wants it */
+      if (!sconn->no_reconnect) {
+        silc_schedule_task_add_timeout(server->schedule,
+                                      silc_server_connect_to_router_retry,
+                                      sconn, 1, 0);
+        silc_dlist_del(server->conns, sconn);
+        return;
+      }
+
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_free_sock_user_data(server, sconn->sock, NULL);
       silc_server_disconnect_remote(server, sconn->sock,
                                    SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
-      silc_server_connection_free(sconn);
-      silc_free(entry);
       return;
     }
 
     /* Registered */
     silc_idlist_add_data(id_entry, (SilcIDListData)entry);
-    idata = (SilcIDListData)entry;
+    idata = (SilcIDListData)id_entry;
     idata->status |= (SILC_IDLIST_STATUS_REGISTERED |
                      SILC_IDLIST_STATUS_LOCAL);
+    idata->sconn = sconn;
+
+    /* Statistics */
+    server->stat.my_routers++;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.routers++;
+    SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
 
     if (!sconn->backup) {
       /* Mark this router our primary router if we're still standalone */
@@ -1459,14 +1514,29 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
          silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
        }
 
-#if 0
        /* If we are backup router then this primary router is whom we are
           backing up. */
-       if (server->server_type == SILC_BACKUP_ROUTER)
-         silc_server_backup_add(server, server->id_entry, sock->ip,
+       if (server->server_type == SILC_BACKUP_ROUTER) {
+         silc_socket_stream_get_info(silc_packet_stream_get_stream(sconn->
+                                                                   sock),
+                                     NULL, NULL, &ip, NULL);
+         silc_server_backup_add(server, server->id_entry, ip,
                                 sconn->remote_port, TRUE);
-#endif /* 0 */
+       }
       }
+#if 0
+         else {
+       /* We already have primary router.  Disconnect this connection */
+       SILC_LOG_DEBUG(("We already have primary router, disconnect"));
+       silc_idlist_del_server(server->global_list, id_entry);
+       if (sconn->callback)
+         (*sconn->callback)(server, NULL, sconn->callback_context);
+       silc_server_free_sock_user_data(server, sconn->sock, NULL);
+       silc_server_disconnect_remote(server, sconn->sock,
+                                     SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+       return;
+      }
+#endif /* 0 */
     } else {
       /* Add this server to be our backup router */
       id_entry->server_type = SILC_BACKUP_ROUTER;
@@ -1477,13 +1547,16 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
     break;
 
   default:
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_AUTH_FAILED, NULL);
-    silc_server_connection_free(sconn);
-    silc_free(entry);
     return;
   }
 
+  SILC_LOG_DEBUG(("Connection established, sock %p", sconn->sock));
+
   conn = sconn->conn.ref_ptr;
   param = &server->config->param;
   if (conn && conn->param)
@@ -1494,28 +1567,16 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
   silc_schedule_task_add_timeout(server->schedule, silc_server_do_rekey,
                                 sconn->sock, sconn->rekey_timeout, 0);
 
-#if 0
-  /* Perform keepalive. */
-  silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
-                           silc_server_perform_heartbeat,
-                           server->schedule);
+  /* Set the entry as packet stream context */
+  silc_packet_set_context(sconn->sock, id_entry);
 
- out:
   /* Call the completion callback to indicate that we've connected to
      the router */
   if (sconn && sconn->callback)
     (*sconn->callback)(server, id_entry, sconn->callback_context);
 
-  /* Free the temporary connection data context */
-  if (sconn) {
-    silc_server_config_unref(&sconn->conn);
-    silc_free(sconn->remote_host);
-    silc_free(sconn->backup_replace_ip);
-    silc_free(sconn);
-  }
   if (sconn == server->router_conn)
     server->router_conn = NULL;
-#endif /* 0 */
 
   silc_free(entry);
 }
@@ -1528,8 +1589,9 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
                                     SilcSKERekeyMaterial rekey,
                                     void *context)
 {
-  SilcServerConnection sconn = context;
-  SilcUnknownEntry entry = silc_packet_get_context(sconn->sock);
+  SilcPacketStream sock = context;
+  SilcUnknownEntry entry = silc_packet_get_context(sock);
+  SilcServerConnection sconn = entry->data.sconn;
   SilcServer server = entry->server;
   SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
   SilcAuthMethod auth_meth = SILC_AUTH_NONE;
@@ -1540,16 +1602,30 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
   SilcHmac hmac_send, hmac_receive;
   SilcHash hash;
 
+  SILC_LOG_DEBUG(("Connection %p, SKE completed, entry %p", sconn, entry));
+
+  entry->op = NULL;
+
   if (status != SILC_SKE_STATUS_OK) {
     /* SKE failed */
     SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
                    silc_ske_map_status(status), entry->hostname, entry->ip));
-
-    /* XXX retry connecting */
     silc_ske_free(ske);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
-    silc_server_connection_free(sconn);
     return;
   }
 
@@ -1558,14 +1634,23 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
   /* Set the keys into use.  The data will be encrypted after this. */
   if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
                         &hmac_send, &hmac_receive, &hash)) {
+    silc_ske_free(ske);
 
-    /* XXX retry connecting */
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
 
     /* Error setting keys */
-    silc_ske_free(ske);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
-    silc_server_connection_free(sconn);
     return;
   }
   silc_packet_set_keys(sconn->sock, send_key, receive_key, hmac_send,
@@ -1576,13 +1661,23 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
   connauth = silc_connauth_alloc(server->schedule, ske,
                                 server->config->conn_auth_timeout);
   if (!connauth) {
-    /* XXX retry connecting */
+    silc_ske_free(ske);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
 
     /** Error allocating auth protocol */
-    silc_ske_free(ske);
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
+    silc_server_free_sock_user_data(server, sconn->sock, NULL);
     silc_server_disconnect_remote(server, sconn->sock,
                                  SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
-    silc_server_connection_free(sconn);
     return;
   }
 
@@ -1603,11 +1698,14 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
     }
   }
 
+  entry->data.rekey = rekey;
+
   /* Start connection authentication */
-  silc_connauth_initiator(connauth, server->server_type == SILC_ROUTER ?
-                         SILC_CONN_ROUTER : SILC_CONN_SERVER, auth_meth,
-                         auth_data, auth_data_len,
-                         silc_server_ke_auth_compl, sconn);
+  entry->op =
+    silc_connauth_initiator(connauth, server->server_type == SILC_SERVER ?
+                           SILC_CONN_SERVER : SILC_CONN_ROUTER, auth_meth,
+                           auth_data, auth_data_len,
+                           silc_server_ke_auth_compl, sconn);
 }
 
 /* Function that is called when the network connection to a router has
@@ -1631,6 +1729,18 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   if (!sconn->sock) {
     SILC_LOG_ERROR(("Cannot connect: cannot create packet stream"));
     silc_stream_destroy(sconn->stream);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1640,6 +1750,18 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   if (!silc_packet_set_ids(sconn->sock, SILC_ID_SERVER, server->id,
                           0, NULL)) {
     silc_packet_stream_destroy(sconn->sock);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1648,12 +1770,27 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   entry = silc_calloc(1, sizeof(*entry));
   if (!entry) {
     silc_packet_stream_destroy(sconn->sock);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
   entry->server = server;
+  entry->data.sconn = sconn;
   silc_packet_set_context(sconn->sock, entry);
 
+  SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
   /* Set Key Exchange flags from configuration, but fall back to global
      settings too. */
   memset(&params, 0, sizeof(params));
@@ -1662,12 +1799,24 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
     params.flags |= SILC_SKE_SP_FLAG_PFS;
 
   /* Start SILC Key Exchange protocol */
-  SILC_LOG_DEBUG(("Starting key exchange protocol"));
+  SILC_LOG_DEBUG(("Starting key exchange protocol, connection %p", sconn));
   ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
-                      server->public_key, server->private_key, sconn->sock);
+                      server->public_key, server->private_key, sconn);
   if (!ske) {
     silc_free(entry);
     silc_packet_stream_destroy(sconn->sock);
+
+    /* Try reconnecting if configuration wants it */
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+      return;
+    }
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1677,7 +1826,7 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   /* Start key exchange protocol */
   params.version = silc_version_string;
   params.timeout_secs = server->config->key_exchange_timeout;
-  silc_ske_initiator(ske, sconn->sock, &params, NULL);
+  entry->op = silc_ske_initiator(ske, sconn->sock, &params, NULL);
 }
 
 /* Timeout callback that will be called to retry connecting to remote
@@ -1710,8 +1859,11 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
 
   /* If we've reached max retry count, give up. */
   if ((sconn->retry_count > param->reconnect_count) &&
-      !param->reconnect_keep_trying) {
+      sconn->no_reconnect) {
     SILC_LOG_ERROR(("Could not connect, giving up"));
+
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1741,7 +1893,7 @@ static void silc_server_connection_established(SilcNetStatus status,
 
   switch (status) {
   case SILC_NET_OK:
-    SILC_LOG_DEBUG(("Connection to %s:%d established",
+    SILC_LOG_DEBUG(("Connection %p to %s:%d established", sconn,
                    sconn->remote_host, sconn->remote_port));
 
     /* Continue with key exchange protocol */
@@ -1754,7 +1906,16 @@ static void silc_server_connection_established(SilcNetStatus status,
     SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
                    sconn->remote_host, sconn->remote_port,
                    silc_net_get_error_string(status)));
-    silc_server_connection_free(sconn);
+    if (!sconn->no_reconnect) {
+      silc_schedule_task_add_timeout(sconn->server->schedule,
+                                    silc_server_connect_to_router_retry,
+                                    sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
+    } else {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
+      silc_server_connection_free(sconn);
+    }
     break;
 
   default:
@@ -1765,7 +1926,10 @@ static void silc_server_connection_established(SilcNetStatus status,
       silc_schedule_task_add_timeout(sconn->server->schedule,
                                     silc_server_connect_to_router_retry,
                                     sconn, 1, 0);
+      silc_dlist_del(server->conns, sconn);
     } else {
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
       silc_server_connection_free(sconn);
     }
     break;
@@ -1784,6 +1948,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
 
   /* Don't connect if we are shutting down. */
   if (server->server_shutdown) {
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1792,7 +1958,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
                 (sconn->backup ? "backup router" : "router"),
                 sconn->remote_host, sconn->remote_port));
 
-  if (!server->no_conf) {
+  if (!sconn->no_conf) {
     /* Find connection configuration */
     rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
                                                sconn->remote_port);
@@ -1800,6 +1966,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
       SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
                     (sconn->backup ? "backup router" : "router"),
                     sconn->remote_host, sconn->remote_port));
+      if (sconn->callback)
+       (*sconn->callback)(server, NULL, sconn->callback_context);
       silc_server_connection_free(sconn);
       return;
     }
@@ -1816,6 +1984,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   if (!sconn->op) {
     SILC_LOG_ERROR(("Could not connect to router %s:%d",
                    sconn->remote_host, sconn->remote_port));
+    if (sconn->callback)
+      (*sconn->callback)(server, NULL, sconn->callback_context);
     silc_server_connection_free(sconn);
     return;
   }
@@ -1834,6 +2004,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   SilcServer server = context;
   SilcServerConnection sconn;
   SilcServerConfigRouter *ptr;
+  SilcServerConfigConnParams *param;
 
   /* Don't connect if we are shutting down. */
   if (server->server_shutdown)
@@ -1844,7 +2015,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
                   "normal server" : server->server_type == SILC_ROUTER ?
                   "router" : "backup router/normal server")));
 
-  /* XXX */
   if (!server->config->routers) {
     /* There wasn't a configured router, we will continue but we don't
        have a connection to outside world.  We will be standalone server. */
@@ -1853,6 +2023,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
     return;
   }
 
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router_retry);
+
   /* Create the connections to all our routes */
   for (ptr = server->config->routers; ptr; ptr = ptr->next) {
 
@@ -1868,13 +2044,16 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 
     if (!ptr->initiator)
       continue;
+    if (ptr->dynamic_connection)
+      continue;
 
     /* Check whether we are connecting or connected to this host already */
     if (silc_server_num_sockets_by_remote(server,
                                          silc_net_is_ip(ptr->host) ?
                                          ptr->host : NULL,
                                          silc_net_is_ip(ptr->host) ?
-                                         NULL : ptr->host, ptr->port)) {
+                                         NULL : ptr->host, ptr->port,
+                                         SILC_CONN_ROUTER)) {
       SILC_LOG_DEBUG(("We are already connected to %s:%d",
                      ptr->host, ptr->port));
 
@@ -1892,11 +2071,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
        if (!sock)
          continue;
        server->backup_noswitch = TRUE;
-#if 0
-       if (sock->user_data)
-         silc_server_free_sock_user_data(server, sock, NULL);
+       silc_server_free_sock_user_data(server, sock, NULL);
        silc_server_disconnect_remote(server, sock, 0, NULL);
-#endif /* 0 */
        server->backup_noswitch = FALSE;
        SILC_LOG_DEBUG(("Reconnecting to primary router"));
       } else {
@@ -1904,10 +2080,13 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       }
     }
 
+    param = (ptr->param ? ptr->param : &server->config->param);
+
     /* Allocate connection object for hold connection specific stuff. */
     sconn = silc_calloc(1, sizeof(*sconn));
     if (!sconn)
       continue;
+    sconn->server = server;
     sconn->remote_host = strdup(ptr->host);
     sconn->remote_port = ptr->port;
     sconn->backup = ptr->backup_router;
@@ -1915,8 +2094,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
       sconn->backup_replace_port = ptr->backup_replace_port;
     }
+    sconn->no_reconnect = param->reconnect_keep_trying == FALSE;
+
+    SILC_LOG_DEBUG(("Created connection %p", sconn));
 
-    /* XXX */
     if (!server->router_conn && !sconn->backup)
       server->router_conn = sconn;
 
@@ -1966,13 +2147,28 @@ silc_server_accept_get_auth(SilcConnAuth connauth,
     if (cconfig->publickeys)
       *repository = server->repository;
 
-    entry->data.conn_type = conn_type;
+    if (cconfig->publickeys) {
+      if (server->config->prefer_passphrase_auth) {
+       *repository = NULL;
+      } else {
+       *passphrase = NULL;
+       *passphrase_len = 0;
+      }
+    }
+
+    entry->conn_type = conn_type;
     return TRUE;
   }
 
   /* Remote end is server */
   if (conn_type == SILC_CONN_SERVER) {
-    SilcServerConfigServer *sconfig = entry->sconfig.ref_ptr;
+    SilcServerConfigServer *sconfig;
+
+    /* If we are normal server, don't accept the connection */
+    if (server->server_type == SILC_SERVER)
+      return FALSE;
+
+    sconfig = entry->sconfig.ref_ptr;
     if (!sconfig)
       return FALSE;
 
@@ -1981,7 +2177,16 @@ silc_server_accept_get_auth(SilcConnAuth connauth,
     if (sconfig->publickeys)
       *repository = server->repository;
 
-    entry->data.conn_type = conn_type;
+    if (sconfig->publickeys) {
+      if (server->config->prefer_passphrase_auth) {
+       *repository = NULL;
+      } else {
+       *passphrase = NULL;
+       *passphrase_len = 0;
+      }
+    }
+
+    entry->conn_type = conn_type;
     return TRUE;
   }
 
@@ -1996,7 +2201,16 @@ silc_server_accept_get_auth(SilcConnAuth connauth,
     if (rconfig->publickeys)
       *repository = server->repository;
 
-    entry->data.conn_type = conn_type;
+    if (rconfig->publickeys) {
+      if (server->config->prefer_passphrase_auth) {
+       *repository = NULL;
+      } else {
+       *passphrase = NULL;
+       *passphrase_len = 0;
+      }
+    }
+
+    entry->conn_type = conn_type;
     return TRUE;
   }
 
@@ -2016,11 +2230,12 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
   SilcServerConfigConnParams *param = &server->config->param;
   SilcServerConnection sconn;
   void *id_entry;
-  const char *hostname;
+  const char *hostname, *ip;
   SilcUInt16 port;
 
+  entry->op = NULL;
   silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
-                             NULL, &hostname, NULL, &port);
+                             NULL, &hostname, &ip, &port);
 
   if (success == FALSE) {
     /* Authentication failed */
@@ -2029,19 +2244,23 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
     server->stat.auth_failures++;
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    silc_server_config_unref(&entry->cconfig);
+    silc_server_config_unref(&entry->sconfig);
+    silc_server_config_unref(&entry->rconfig);
+    silc_server_free_sock_user_data(server, sock, NULL);
     goto out;
   }
 
   SILC_LOG_DEBUG(("Checking whether connection is allowed"));
 
-  switch (entry->data.conn_type) {
+  switch (entry->conn_type) {
   case SILC_CONN_CLIENT:
     {
       SilcClientEntry client;
       SilcServerConfigClient *conn = entry->cconfig.ref_ptr;
 
       /* Verify whether this connection is after all allowed to connect */
-      if (!silc_server_connection_allowed(server, sock, entry->data.conn_type,
+      if (!silc_server_connection_allowed(server, sock, entry->conn_type,
                                          &server->config->param,
                                          conn->param,
                                          silc_connauth_get_ske(connauth))) {
@@ -2067,6 +2286,10 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
                                        SILC_STATUS_ERR_PERM_DENIED,
                                        "We do not have connection to backup "
                                        "router established, try later");
+         silc_server_config_unref(&entry->cconfig);
+         silc_server_config_unref(&entry->sconfig);
+         silc_server_config_unref(&entry->rconfig);
+         silc_server_free_sock_user_data(server, sock, NULL);
          server->stat.auth_failures++;
 
          /* From here on, wait 20 seconds for the backup router to appear. */
@@ -2091,9 +2314,14 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
        server->stat.auth_failures++;
        silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_AUTH_FAILED, NULL);
+       silc_server_config_unref(&entry->cconfig);
+       silc_server_config_unref(&entry->sconfig);
+       silc_server_config_unref(&entry->rconfig);
+       silc_server_free_sock_user_data(server, sock, NULL);
        goto out;
       }
       entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+      entry->data.conn_type = SILC_CONN_CLIENT;
 
       /* Statistics */
       server->stat.my_clients++;
@@ -2121,6 +2349,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
       }
 
       /* Add public key to repository */
+      SILC_LOG_DEBUG(("Add client public key to repository"));
       if (!silc_server_get_public_key_by_client(server, client, NULL))
        silc_skr_add_public_key_simple(server->repository,
                                       entry->data.public_key,
@@ -2140,14 +2369,14 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
       SilcBool backup_router = FALSE;
       char *backup_replace_ip = NULL;
       SilcUInt16 backup_replace_port = 0;
-      SilcServerConfigServer *sconn = entry->sconfig.ref_ptr;
+      SilcServerConfigServer *srvconn = entry->sconfig.ref_ptr;
       SilcServerConfigRouter *rconn = entry->rconfig.ref_ptr;
 
       /* If we are backup router and this is incoming server connection
         and we do not have connection to primary router, do not allow
         the connection. */
       if (server->server_type == SILC_BACKUP_ROUTER &&
-         entry->data.conn_type == SILC_CONN_SERVER &&
+         entry->conn_type == SILC_CONN_SERVER &&
          !SILC_PRIMARY_ROUTE(server)) {
        SILC_LOG_INFO(("Will not accept server connection because we do "
                       "not have primary router connection established"));
@@ -2155,17 +2384,24 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
                                      SILC_STATUS_ERR_PERM_DENIED,
                                      "We do not have connection to primary "
                                      "router established, try later");
+       silc_server_config_unref(&entry->cconfig);
+       silc_server_config_unref(&entry->sconfig);
+       silc_server_config_unref(&entry->rconfig);
+       silc_server_free_sock_user_data(server, sock, NULL);
        server->stat.auth_failures++;
        goto out;
       }
 
-      if (entry->data.conn_type == SILC_CONN_ROUTER) {
+      if (entry->conn_type == SILC_CONN_ROUTER) {
        /* Verify whether this connection is after all allowed to connect */
        if (!silc_server_connection_allowed(server, sock,
-                                           entry->data.conn_type,
+                                           entry->conn_type,
                                            &server->config->param,
                                            rconn ? rconn->param : NULL,
                                            silc_connauth_get_ske(connauth))) {
+         silc_server_config_unref(&entry->cconfig);
+         silc_server_config_unref(&entry->sconfig);
+         silc_server_config_unref(&entry->rconfig);
          server->stat.auth_failures++;
          goto out;
        }
@@ -2194,19 +2430,19 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
        }
       }
 
-      if (entry->data.conn_type == SILC_CONN_SERVER) {
+      if (entry->conn_type == SILC_CONN_SERVER) {
        /* Verify whether this connection is after all allowed to connect */
        if (!silc_server_connection_allowed(server, sock,
-                                           entry->data.conn_type,
+                                           entry->conn_type,
                                            &server->config->param,
-                                           sconn ? sconn->param : NULL,
+                                           srvconn ? srvconn->param : NULL,
                                            silc_connauth_get_ske(connauth))) {
          server->stat.auth_failures++;
          goto out;
        }
-       if (sconn) {
-         if (sconn->param) {
-           param = sconn->param;
+       if (srvconn) {
+         if (srvconn->param) {
+           param = srvconn->param;
 
            if (!param->keepalive_secs)
              param->keepalive_secs = server->config->param.keepalive_secs;
@@ -2220,14 +2456,13 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
            }
          }
 
-         backup_router = sconn->backup_router;
+         backup_router = srvconn->backup_router;
        }
       }
 
       /* If we are primary router and we have backup router configured
         but it has not connected to use yet, do not accept any other
         connection. */
-#if 0
       if (server->wait_backup && server->server_type == SILC_ROUTER &&
          !server->backup_router && !backup_router) {
        SilcServerConfigRouter *router;
@@ -2243,6 +2478,10 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
                                        SILC_STATUS_ERR_PERM_DENIED,
                                        "We do not have connection to backup "
                                        "router established, try later");
+         silc_server_config_unref(&entry->cconfig);
+         silc_server_config_unref(&entry->sconfig);
+         silc_server_config_unref(&entry->rconfig);
+         silc_server_free_sock_user_data(server, sock, NULL);
          server->stat.auth_failures++;
 
          /* From here on, wait 20 seconds for the backup router to appear. */
@@ -2252,14 +2491,13 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
          goto out;
        }
       }
-#endif /* 0 */
 
       SILC_LOG_DEBUG(("Remote host is %s",
-                     entry->data.conn_type == SILC_CONN_SERVER ?
+                     entry->conn_type == SILC_CONN_SERVER ?
                      "server" : (backup_router ?
                                  "backup router" : "router")));
       SILC_LOG_INFO(("Connection %s (%s) is %s", entry->hostname,
-                    entry->ip, entry->data.conn_type == SILC_CONN_SERVER ?
+                    entry->ip, entry->conn_type == SILC_CONN_SERVER ?
                     "server" : (backup_router ?
                                 "backup router" : "router")));
 
@@ -2268,15 +2506,15 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
         server. We mark ourselves as router for this server if we really
         are router. */
       new_server =
-       silc_idlist_add_server((entry->data.conn_type == SILC_CONN_SERVER ?
+       silc_idlist_add_server((entry->conn_type == SILC_CONN_SERVER ?
                                server->local_list : (backup_router ?
                                                      server->local_list :
                                                      server->global_list)),
                               NULL,
-                              (entry->data.conn_type == SILC_CONN_SERVER ?
+                              (entry->conn_type == SILC_CONN_SERVER ?
                                SILC_SERVER : SILC_ROUTER),
                               NULL,
-                              (entry->data.conn_type == SILC_CONN_SERVER ?
+                              (entry->conn_type == SILC_CONN_SERVER ?
                                server->id_entry : (backup_router ?
                                                    server->id_entry : NULL)),
                               sock);
@@ -2284,10 +2522,15 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
        SILC_LOG_ERROR(("Could not add new server to cache"));
        silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_AUTH_FAILED, NULL);
+       silc_server_config_unref(&entry->cconfig);
+       silc_server_config_unref(&entry->sconfig);
+       silc_server_config_unref(&entry->rconfig);
+       silc_server_free_sock_user_data(server, sock, NULL);
        server->stat.auth_failures++;
        goto out;
       }
       entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+      entry->data.conn_type = entry->conn_type;
 
       id_entry = (void *)new_server;
 
@@ -2313,9 +2556,11 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
       if (entry->data.conn_type == SILC_CONN_SERVER) {
        server->stat.my_servers++;
        server->stat.servers++;
+       SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
       } else {
        server->stat.my_routers++;
        server->stat.routers++;
+       SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
       }
 
       /* Check whether this connection is to be our primary router connection
@@ -2357,15 +2602,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
   silc_packet_set_context(sock, id_entry);
 
   /* Connection has been fully established now. Everything is ok. */
-  SILC_LOG_DEBUG(("New connection authenticated"));
-
-#if 0
-  /* Perform keepalive. */
-  if (param->keepalive_secs)
-    silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
-                             silc_server_perform_heartbeat,
-                             server->schedule);
-#endif
+  SILC_LOG_DEBUG(("New connection %p authenticated", sconn));
 
   /* Perform Quality of Service */
   if (param->qos)
@@ -2400,6 +2637,10 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
   SilcCipher send_key, receive_key;
   SilcHmac hmac_send, hmac_receive;
   SilcHash hash;
+  unsigned char *pk;
+  SilcUInt32 pk_len;
+
+  entry->op = NULL;
 
   if (status != SILC_SKE_STATUS_OK) {
     /* SKE failed */
@@ -2408,6 +2649,10 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
     silc_ske_free(ske);
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    silc_server_config_unref(&entry->cconfig);
+    silc_server_config_unref(&entry->sconfig);
+    silc_server_config_unref(&entry->rconfig);
+    silc_server_free_sock_user_data(server, sock, NULL);
     return;
   }
 
@@ -2420,6 +2665,7 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
     silc_ske_free(ske);
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+    silc_server_free_sock_user_data(server, sock, NULL);
     return;
   }
   silc_packet_set_keys(sock, send_key, receive_key, hmac_send,
@@ -2427,6 +2673,10 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
 
   idata->rekey = rekey;
   idata->public_key = silc_pkcs_public_key_copy(prop->public_key);
+  pk = silc_pkcs_public_key_encode(idata->public_key, &pk_len);
+  silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
+
+  silc_hash_alloc(silc_hash_get_name(prop->hash), &idata->hash);
 
   SILC_LOG_DEBUG(("Starting connection authentication"));
   server->stat.auth_attempts++;
@@ -2438,12 +2688,17 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
     silc_ske_free(ske);
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    silc_server_config_unref(&entry->cconfig);
+    silc_server_config_unref(&entry->sconfig);
+    silc_server_config_unref(&entry->rconfig);
+    silc_server_free_sock_user_data(server, sock, NULL);
     return;
   }
 
   /* Start connection authentication */
-  silc_connauth_responder(connauth, silc_server_accept_get_auth,
-                         silc_server_accept_auth_compl, sock);
+  entry->op =
+    silc_connauth_responder(connauth, silc_server_accept_get_auth,
+                           silc_server_accept_auth_compl, sock);
 }
 
 /* Accept new TCP connection */
@@ -2496,6 +2751,8 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
   }
   server->stat.conn_num++;
 
+  SILC_LOG_DEBUG(("Created packet stream %p", packet_stream));
+
   /* Set source ID to packet stream */
   if (!silc_packet_set_ids(packet_stream, SILC_ID_SERVER, server->id,
                           0, NULL)) {
@@ -2515,6 +2772,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
     silc_server_disconnect_remote(server, packet_stream,
                                  SILC_STATUS_ERR_BANNED_FROM_SERVER,
                                  deny->reason);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
     return;
   }
 
@@ -2533,6 +2791,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
     server->stat.conn_failures++;
     silc_server_disconnect_remote(server, packet_stream,
                                  SILC_STATUS_ERR_BANNED_FROM_SERVER, NULL);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
     return;
   }
 
@@ -2542,14 +2801,18 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
     server->stat.conn_failures++;
     silc_server_disconnect_remote(server, packet_stream,
                                  SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
     return;
   }
   entry->hostname = hostname;
   entry->ip = ip;
   entry->port = port;
   entry->server = server;
+  entry->data.conn_type = SILC_CONN_UNKNOWN;
   silc_packet_set_context(packet_stream, entry);
 
+  SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
   silc_server_config_ref(&entry->cconfig, server->config, cconfig);
   silc_server_config_ref(&entry->sconfig, server->config, sconfig);
   silc_server_config_ref(&entry->rconfig, server->config, rconfig);
@@ -2576,6 +2839,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
     server->stat.conn_failures++;
     silc_server_disconnect_remote(server, packet_stream,
                                  SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+    silc_server_free_sock_user_data(server, packet_stream, NULL);
     return;
   }
   silc_ske_set_callbacks(ske, silc_server_verify_key,
@@ -2584,7 +2848,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
   /* Start key exchange protocol */
   params.version = silc_version_string;
   params.timeout_secs = server->config->key_exchange_timeout;
-  silc_ske_responder(ske, packet_stream, &params);
+  entry->op = silc_ske_responder(ske, packet_stream, &params);
 }
 
 
@@ -2631,6 +2895,8 @@ SILC_TASK_CALLBACK(silc_server_do_rekey)
   SilcIDListData idata = silc_packet_get_context(sock);
   SilcSKE ske;
 
+  SILC_LOG_DEBUG(("Perform rekey, sock %p", sock));
+
   /* Do not execute rekey with disabled connections */
   if (idata->status & SILC_IDLIST_STATUS_DISABLED)
     return;
@@ -2648,8 +2914,8 @@ SILC_TASK_CALLBACK(silc_server_do_rekey)
                  SILC_CONNTYPE_STRING(idata->conn_type)));
 
   /* Allocate SKE */
-  ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
-                      server->public_key, server->private_key, sock);
+  ske = silc_ske_alloc(server->rng, server->schedule, NULL,
+                      server->public_key, NULL, sock);
   if (!ske)
     return;
 
@@ -2696,13 +2962,13 @@ static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
   SilcIDListData idata = silc_packet_get_context(sock);
   SilcSKE ske;
 
-  SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s]",
+  SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s], sock %p",
                  idata->sconn->remote_host, idata->sconn->remote_port,
-                 SILC_CONNTYPE_STRING(idata->conn_type)));
+                 SILC_CONNTYPE_STRING(idata->conn_type), sock));
 
   /* Allocate SKE */
-  ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
-                      server->public_key, server->private_key, sock);
+  ske = silc_ske_alloc(server->rng, server->schedule, NULL,
+                      server->public_key, NULL, sock);
   if (!ske) {
     silc_packet_free(packet);
     return;
@@ -2723,7 +2989,7 @@ static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
 
 SILC_TASK_CALLBACK(silc_server_close_connection_final)
 {
-  silc_packet_stream_destroy(context);
+  silc_packet_stream_unref(context);
 }
 
 /* Closes connection to socket connection */
@@ -2736,19 +3002,8 @@ void silc_server_close_connection(SilcServer server,
   const char *hostname;
   SilcUInt16 port;
 
-#if 0
-  /* If any protocol is active cancel its execution. It will call
-     the final callback which will finalize the disconnection. */
-  if (sock->protocol && sock->protocol->protocol &&
-      sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
-    SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
-    silc_protocol_cancel(sock->protocol, server->schedule);
-    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-    silc_protocol_execute_final(sock->protocol, server->schedule);
-    sock->protocol = NULL;
+  if (!silc_packet_stream_is_valid(sock))
     return;
-  }
-#endif
 
   memset(tmp, 0, sizeof(tmp));
   //  silc_socket_get_error(sock, tmp, sizeof(tmp));
@@ -2760,8 +3015,20 @@ void silc_server_close_connection(SilcServer server,
 
   //  silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
 
+  if (idata && idata->sconn) {
+    silc_server_connection_free(idata->sconn);
+    idata->sconn = NULL;
+  }
+
+  /* Take a reference and then destroy the stream.  The last reference
+     is released later in a timeout callback. */
+  silc_packet_stream_ref(sock);
+  silc_packet_stream_destroy(sock);
+
   /* Close connection with timeout */
   server->stat.conn_num--;
+  silc_schedule_task_del_by_all(server->schedule, 0,
+                               silc_server_close_connection_final, sock);
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_close_connection_final,
                                 sock, 0, 1);
@@ -2781,7 +3048,7 @@ void silc_server_disconnect_remote(SilcServer server,
   if (!sock)
     return;
 
-  SILC_LOG_DEBUG(("Disconnecting remote host"));
+  SILC_LOG_DEBUG(("Disconnecting remote host, sock %p", sock));
 
   va_start(ap, status);
   cp = va_arg(ap, char *);
@@ -2799,16 +3066,6 @@ void silc_server_disconnect_remote(SilcServer server,
   silc_server_close_connection(server, sock);
 }
 
-SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
-{
-  SilcClientEntry client = context;
-
-  assert(!silc_hash_table_count(client->channels));
-
-  silc_idlist_del_data(client);
-  //  silc_idcache_purge_by_context(server->local_list->clients, client);
-}
-
 /* Frees client data and notifies about client's signoff. */
 
 void silc_server_free_client_data(SilcServer server,
@@ -2817,7 +3074,7 @@ void silc_server_free_client_data(SilcServer server,
                                  int notify,
                                  const char *signoff)
 {
-  SILC_LOG_DEBUG(("Freeing client data"));
+  SILC_LOG_DEBUG(("Freeing client %p data", client));
 
   if (client->id) {
     /* Check if anyone is watching this nickname */
@@ -2859,20 +3116,25 @@ void silc_server_free_client_data(SilcServer server,
   SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
   silc_schedule_task_del_by_context(server->schedule, client);
 
+  if (client->data.sconn) {
+    silc_server_connection_free(client->data.sconn);
+    client->data.sconn = NULL;
+  }
+
   /* We will not delete the client entry right away. We will take it
      into history (for WHOWAS command) for 5 minutes, unless we're
      shutting down server. */
   if (!server->server_shutdown) {
-    silc_schedule_task_add_timeout(server->schedule,
-                                  silc_server_free_client_data_timeout,
-                                  client, 600, 0);
     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
-    client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
     client->mode = 0;
     client->router = NULL;
     client->connection = NULL;
+    client->data.created = silc_time();
+    silc_dlist_del(server->expired_clients, client);
+    silc_dlist_add(server->expired_clients, client);
   } else {
     /* Delete directly since we're shutting down server */
+    SILC_LOG_DEBUG(("Delete client directly"));
     silc_idlist_del_data(client);
     silc_idlist_del_client(server->local_list, client);
   }
@@ -2886,11 +3148,35 @@ void silc_server_free_sock_user_data(SilcServer server,
                                     SilcPacketStream sock,
                                     const char *signoff_message)
 {
-  SilcIDListData idata = silc_packet_get_context(sock);
+  SilcIDListData idata;
+  const char *ip;
+  SilcUInt16 port;
 
+  if (!sock)
+    return;
+
+  SILC_LOG_DEBUG(("Start, sock %p", sock));
+
+  idata = silc_packet_get_context(sock);
   if (!idata)
     return;
 
+  silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_rekey,
+                               sock);
+
+  /* Cancel active protocols */
+  if (idata) {
+    if (idata->sconn && idata->sconn->op) {
+      SILC_LOG_DEBUG(("Abort active protocol"));
+      silc_async_abort(idata->sconn->op, NULL, NULL);
+    }
+    if (idata->conn_type == SILC_CONN_UNKNOWN &&
+        ((SilcUnknownEntry)idata)->op) {
+      SILC_LOG_DEBUG(("Abort active protocol"));
+      silc_async_abort(((SilcUnknownEntry)idata)->op, NULL, NULL);
+    }
+  }
+
   switch (idata->conn_type) {
   case SILC_CONN_CLIENT:
     {
@@ -2907,7 +3193,7 @@ void silc_server_free_sock_user_data(SilcServer server,
       SilcServerEntry user_data = (SilcServerEntry)idata;
       SilcServerEntry backup_router = NULL;
 
-      SILC_LOG_DEBUG(("Freeing server data"));
+      SILC_LOG_DEBUG(("Freeing server %p data", user_data));
 
       if (user_data->id)
        backup_router = silc_server_backup_get(server, user_data->id);
@@ -2920,6 +3206,9 @@ void silc_server_free_sock_user_data(SilcServer server,
       if (server->server_shutdown || server->backup_noswitch)
        backup_router = NULL;
 
+      silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                                 NULL, NULL, &ip, &port);
+
       /* If this was our primary router connection then we're lost to
         the outside world. */
       if (server->router == user_data) {
@@ -2956,17 +3245,15 @@ void silc_server_free_sock_user_data(SilcServer server,
          /* We stop here to take a breath */
          sleep(2);
 
-#if 0
          if (server->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_create_connection(server, TRUE, FALSE, ip, port,
                                         silc_server_backup_connected,
                                         NULL);
          }
-#endif /* 0 */
 
          /* Mark this connection as replaced */
          silc_server_backup_replaced_add(server, user_data->id,
@@ -3058,22 +3345,31 @@ void silc_server_free_sock_user_data(SilcServer server,
       }
       server->backup_noswitch = FALSE;
 
-      /* Free the server entry */
-      silc_server_backup_del(server, user_data);
-      silc_server_backup_replaced_del(server, user_data);
-      silc_idlist_del_data(user_data);
-      if (!silc_idlist_del_server(server->local_list, user_data))
-       silc_idlist_del_server(server->global_list, user_data);
+      if (idata->sconn) {
+       silc_server_connection_free(idata->sconn);
+       idata->sconn = NULL;
+      }
+
+      /* Statistics */
       if (idata->conn_type == SILC_CONN_SERVER) {
        server->stat.my_servers--;
        server->stat.servers--;
-      } else {
+       SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+      } else if (idata->conn_type == SILC_CONN_ROUTER) {
        server->stat.my_routers--;
        server->stat.routers--;
+       SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
       }
       if (server->server_type == SILC_ROUTER)
        server->stat.cell_servers--;
 
+      /* Free the server entry */
+      silc_server_backup_del(server, user_data);
+      silc_server_backup_replaced_del(server, user_data);
+      silc_idlist_del_data(user_data);
+      if (!silc_idlist_del_server(server->local_list, user_data))
+       silc_idlist_del_server(server->global_list, user_data);
+
       if (backup_router && backup_router != server->id_entry) {
        /* Announce all of our stuff that was created about 5 minutes ago.
           The backup router knows all the other stuff already. */
@@ -3096,8 +3392,18 @@ void silc_server_free_sock_user_data(SilcServer server,
     {
       SilcUnknownEntry entry = (SilcUnknownEntry)idata;
 
-      SILC_LOG_DEBUG(("Freeing unknown connection data"));
+      SILC_LOG_DEBUG(("Freeing unknown connection data %p", entry));
+
+      if (idata->sconn) {
+       if (server->router_conn == idata->sconn) {
+         if (!server->no_reconnect)
+           silc_server_create_connections(server);
+         server->router_conn = NULL;
+       }
 
+       silc_server_connection_free(idata->sconn);
+       idata->sconn = NULL;
+      }
       silc_idlist_del_data(idata);
       silc_free(entry);
       silc_packet_set_context(sock, NULL);
@@ -3770,6 +4076,7 @@ static void silc_server_announce_get_servers(SilcServer server,
   SilcIDCacheEntry id_cache;
   SilcServerEntry entry;
   SilcBuffer idp;
+  void *tmp;
 
   /* Go through all clients in the list */
   if (silc_idcache_get_all(id_list->servers, &list)) {
@@ -3786,11 +4093,14 @@ static void silc_server_announce_get_servers(SilcServer server,
 
       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
 
-      *servers = silc_buffer_realloc(*servers,
-                                    (*servers ?
-                                     silc_buffer_truelen((*servers)) +
-                                     silc_buffer_len(idp) :
-                                     silc_buffer_len(idp)));
+      tmp = silc_buffer_realloc(*servers,
+                               (*servers ?
+                                silc_buffer_truelen((*servers)) +
+                                silc_buffer_len(idp) :
+                                silc_buffer_len(idp)));
+      if (!tmp)
+       return;
+      *servers = tmp;
       silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
       silc_buffer_put(*servers, idp->data, silc_buffer_len(idp));
       silc_buffer_pull(*servers, silc_buffer_len(idp));
@@ -3864,6 +4174,7 @@ static void silc_server_announce_get_clients(SilcServer server,
   SilcBuffer idp;
   SilcBuffer tmp;
   unsigned char mode[4];
+  void *tmp2;
 
   /* Go through all clients in the list */
   if (silc_idcache_get_all(id_list->clients, &list)) {
@@ -3878,13 +4189,21 @@ static void silc_server_announce_get_clients(SilcServer server,
       if (!client->connection && !client->router)
        continue;
 
-      idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+      SILC_LOG_DEBUG(("Announce Client ID %s",
+                     silc_id_render(client->id, SILC_ID_CLIENT)));
 
-      *clients = silc_buffer_realloc(*clients,
-                                    (*clients ?
-                                     silc_buffer_truelen((*clients)) +
-                                     silc_buffer_len(idp) :
-                                     silc_buffer_len(idp)));
+      idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+      if (!idp)
+       return;
+
+      tmp2 = silc_buffer_realloc(*clients,
+                               (*clients ?
+                                silc_buffer_truelen((*clients)) +
+                                silc_buffer_len(idp) :
+                                silc_buffer_len(idp)));
+      if (!tmp2)
+       return;
+      *clients = tmp2;
       silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
       silc_buffer_put(*clients, idp->data, silc_buffer_len(idp));
       silc_buffer_pull(*clients, silc_buffer_len(idp));
@@ -3894,11 +4213,14 @@ static void silc_server_announce_get_clients(SilcServer server,
        silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
                                           2, idp->data, silc_buffer_len(idp),
                                           mode, 4);
-      *umodes = silc_buffer_realloc(*umodes,
-                                   (*umodes ?
-                                    silc_buffer_truelen((*umodes)) +
-                                    silc_buffer_len(tmp) :
-                                    silc_buffer_len(tmp)));
+      tmp2 = silc_buffer_realloc(*umodes,
+                                (*umodes ?
+                                 silc_buffer_truelen((*umodes)) +
+                                 silc_buffer_len(tmp) :
+                                 silc_buffer_len(tmp)));
+      if (!tmp2)
+       return;
+      *umodes = tmp2;
       silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
       silc_buffer_put(*umodes, tmp->data, silc_buffer_len(tmp));
       silc_buffer_pull(*umodes, silc_buffer_len(tmp));
@@ -3985,6 +4307,7 @@ void silc_server_announce_get_inviteban(SilcServer server,
 {
   SilcBuffer list, idp, idp2, tmp2;
   SilcUInt32 type;
+  void *ptype;
   SilcHashTableList htl;
   const unsigned char a[1] = { 0x03 };
 
@@ -3996,9 +4319,10 @@ void silc_server_announce_get_inviteban(SilcServer server,
     type = silc_hash_table_count(channel->invite_list);
     SILC_PUT16_MSB(type, list->data);
     silc_hash_table_list(channel->invite_list, &htl);
-    while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
-      list = silc_argument_payload_encode_one(list, tmp2->data, silc_buffer_len(tmp2),
-                                              type);
+    while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data,
+                                             silc_buffer_len(tmp2),
+                                             SILC_PTR_TO_32(ptype));
     silc_hash_table_list_reset(&htl);
 
     idp2 = silc_id_payload_encode(server->id, SILC_ID_SERVER);
@@ -4020,9 +4344,10 @@ void silc_server_announce_get_inviteban(SilcServer server,
     type = silc_hash_table_count(channel->ban_list);
     SILC_PUT16_MSB(type, list->data);
     silc_hash_table_list(channel->ban_list, &htl);
-    while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
-      list = silc_argument_payload_encode_one(list, tmp2->data, silc_buffer_len(tmp2),
-                                              type);
+    while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data,
+                                             silc_buffer_len(tmp2),
+                                             SILC_PTR_TO_32(ptype));
     silc_hash_table_list_reset(&htl);
 
     *ban =
@@ -4051,6 +4376,7 @@ void silc_server_announce_get_channel_users(SilcServer server,
   int len;
   unsigned char mode[4], ulimit[4];
   char *hmac;
+  void *tmp2;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -4087,10 +4413,13 @@ void silc_server_announce_get_channel_users(SilcServer server,
                                        SILC_CHANNEL_MODE_ULIMIT ?
                                        sizeof(ulimit) : 0));
   len = silc_buffer_len(tmp);
-  *channel_modes =
+  tmp2 =
     silc_buffer_realloc(*channel_modes,
                        (*channel_modes ?
                         silc_buffer_truelen((*channel_modes)) + len : len));
+  if (!tmp2)
+    return;
+  *channel_modes = tmp2;
   silc_buffer_pull_tail(*channel_modes,
                        ((*channel_modes)->end -
                         (*channel_modes)->data));
@@ -4105,6 +4434,9 @@ void silc_server_announce_get_channel_users(SilcServer server,
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
 
+    SILC_LOG_DEBUG(("JOIN Client %s", silc_id_render(chl->client->id,
+                                                    SILC_ID_CLIENT)));
+
     /* JOIN Notify */
     tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2,
                                             clidp->data,
@@ -4112,10 +4444,13 @@ void silc_server_announce_get_channel_users(SilcServer server,
                                             chidp->data,
                                             silc_buffer_len(chidp));
     len = silc_buffer_len(tmp);
-    *channel_users =
+    tmp2 =
       silc_buffer_realloc(*channel_users,
                          (*channel_users ?
                           silc_buffer_truelen((*channel_users)) + len : len));
+    if (!tmp2)
+      return;
+    *channel_users = tmp2;
     silc_buffer_pull_tail(*channel_users,
                          ((*channel_users)->end -
                           (*channel_users)->data));
@@ -4137,11 +4472,14 @@ void silc_server_announce_get_channel_users(SilcServer server,
                                             fkey ? fkey->data : NULL,
                                             fkey ? silc_buffer_len(fkey) : 0);
     len = silc_buffer_len(tmp);
-    *channel_users_modes =
+    tmp2 =
       silc_buffer_realloc(*channel_users_modes,
                          (*channel_users_modes ?
                           silc_buffer_truelen((*channel_users_modes)) +
                           len : len));
+    if (!tmp2)
+      return;
+    *channel_users_modes = tmp2;
     silc_buffer_pull_tail(*channel_users_modes,
                          ((*channel_users_modes)->end -
                           (*channel_users_modes)->data));
@@ -4183,6 +4521,7 @@ void silc_server_announce_get_channels(SilcServer server,
   SilcUInt16 name_len;
   int len;
   int i = *channel_users_modes_c;
+  void *tmp;
   SilcBool announce;
 
   SILC_LOG_DEBUG(("Start"));
@@ -4198,16 +4537,23 @@ void silc_server_announce_get_channels(SilcServer server,
       else
        announce = TRUE;
 
+      SILC_LOG_DEBUG(("Announce Channel ID %s",
+                     silc_id_render(channel->id, SILC_ID_CHANNEL)));
+
       silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
       name_len = strlen(channel->channel_name);
 
       if (announce) {
        len = 4 + name_len + id_len + 4;
-       *channels =
+       tmp =
          silc_buffer_realloc(*channels,
                              (*channels ?
                               silc_buffer_truelen((*channels)) +
                               len : len));
+       if (!tmp)
+         break;
+       *channels = tmp;
+
        silc_buffer_pull_tail(*channels,
                              ((*channels)->end - (*channels)->data));
        silc_buffer_format(*channels,
@@ -4228,15 +4574,23 @@ void silc_server_announce_get_channels(SilcServer server,
 
       if (announce) {
        /* Channel user modes */
-       *channel_users_modes = silc_realloc(*channel_users_modes,
-                                           sizeof(**channel_users_modes) *
-                                           (i + 1));
+       tmp = silc_realloc(*channel_users_modes,
+                           sizeof(**channel_users_modes) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_users_modes = tmp;
        (*channel_users_modes)[i] = NULL;
-       *channel_modes = silc_realloc(*channel_modes,
-                                     sizeof(**channel_modes) * (i + 1));
+       tmp = silc_realloc(*channel_modes,
+                          sizeof(**channel_modes) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_modes = tmp;
        (*channel_modes)[i] = NULL;
-       *channel_ids = silc_realloc(*channel_ids,
-                                     sizeof(**channel_ids) * (i + 1));
+       tmp = silc_realloc(*channel_ids,
+                          sizeof(**channel_ids) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_ids = tmp;
        (*channel_ids)[i] = NULL;
        silc_server_announce_get_channel_users(server, channel,
                                               &(*channel_modes)[i],
@@ -4245,18 +4599,27 @@ void silc_server_announce_get_channels(SilcServer server,
        (*channel_ids)[i] = channel->id;
 
        /* Channel's topic */
-       *channel_topics = silc_realloc(*channel_topics,
-                                      sizeof(**channel_topics) * (i + 1));
+       tmp = silc_realloc(*channel_topics,
+                          sizeof(**channel_topics) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_topics = tmp;
        (*channel_topics)[i] = NULL;
        silc_server_announce_get_channel_topic(server, channel,
                                               &(*channel_topics)[i]);
 
        /* Channel's invite and ban list */
-       *channel_invites = silc_realloc(*channel_invites,
-                                       sizeof(**channel_invites) * (i + 1));
+       tmp = silc_realloc(*channel_invites,
+                          sizeof(**channel_invites) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_invites = tmp;
        (*channel_invites)[i] = NULL;
-       *channel_bans = silc_realloc(*channel_bans,
-                                    sizeof(**channel_bans) * (i + 1));
+       tmp = silc_realloc(*channel_bans,
+                          sizeof(**channel_bans) * (i + 1));
+       if (!tmp)
+         break;
+       *channel_bans = tmp;
        (*channel_bans)[i] = NULL;
        silc_server_announce_get_inviteban(server, channel,
                                           &(*channel_invites)[i],
@@ -4502,10 +4865,10 @@ void silc_server_announce_watches(SilcServer server,
 /* Assembles user list and users mode list from the `channel'. */
 
 SilcBool silc_server_get_users_on_channel(SilcServer server,
-                                     SilcChannelEntry channel,
-                                     SilcBuffer *user_list,
-                                     SilcBuffer *mode_list,
-                                     SilcUInt32 *user_count)
+                                         SilcChannelEntry channel,
+                                         SilcBuffer *user_list,
+                                         SilcBuffer *mode_list,
+                                         SilcUInt32 *user_count)
 {
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
@@ -4919,15 +5282,17 @@ SILC_TASK_CALLBACK(silc_server_get_stats)
     SILC_LOG_DEBUG(("Retrieving stats from router"));
     server->stat.commands_sent++;
     idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
-    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
-                                           ++server->cmd_ident, 1,
-                                           1, idp->data,
-                                           silc_buffer_len(idp));
-    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
-                           SILC_PACKET_COMMAND, 0, packet->data,
-                           silc_buffer_len(packet));
-    silc_buffer_free(packet);
-    silc_buffer_free(idp);
+    if (idp) {
+      packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+                                             ++server->cmd_ident, 1,
+                                             1, idp->data,
+                                             silc_buffer_len(idp));
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                             SILC_PACKET_COMMAND, 0, packet->data,
+                             silc_buffer_len(packet));
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+    }
   }
 
   silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,