Send disconnect packet before freeing socket data in
[silc.git] / apps / silcd / server.c
index 4abb18cb201fd6579ec9f6518016399f9b1e512e..7ee66b1fe8388b342a2b893db9ed7924b21bacfb 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  server.c
+  server.c 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
@@ -8,8 +8,7 @@
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
+  the Free Software Foundation; version 2 of the License.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -78,6 +77,9 @@ int silc_server_alloc(SilcServer *new_server)
 
 void silc_server_free(SilcServer server)
 {
+  SilcIDCacheList list;
+  SilcIDCacheEntry cache;
+
   if (!server)
     return;
 
@@ -93,6 +95,7 @@ void silc_server_free(SilcServer server)
   }
 #endif
 
+  silc_server_backup_free(server);
   silc_server_config_unref(&server->config_ref);
   if (server->rng)
     silc_rng_free(server->rng);
@@ -107,6 +110,66 @@ void silc_server_free(SilcServer server)
   if (server->id_entry)
     silc_idlist_del_server(server->local_list, server->id_entry);
 
+  /* Delete all channels */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->channels, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_channel(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_channel(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->channels, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_channel(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_channel(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
+  /* Delete all clients */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->clients, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_client(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_client(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->clients, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_client(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_client(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
+  /* Delete all servers */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->servers, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_server(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_server(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->servers, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_server(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_server(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
   silc_idcache_free(server->local_list->clients);
   silc_idcache_free(server->local_list->servers);
   silc_idcache_free(server->local_list->channels);
@@ -138,7 +201,7 @@ static bool silc_server_listen(SilcServer server, const char *server_ip,
 {
   *sock = silc_net_create_server(port, server_ip);
   if (*sock < 0) {
-    SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
+    SILC_SERVER_LOG_ERROR(("Could not create server listener: %s on %hu",
                        server_ip, port));
     return FALSE;
   }
@@ -189,10 +252,10 @@ bool silc_server_init_secondary(SilcServer server)
 
     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);
+                          silc_server_accept_new_connection,
+                          (void *)server, 0, 0,
+                          SILC_TASK_FD,
+                          SILC_TASK_PRI_NORMAL);
   }
 
   return TRUE;
@@ -222,7 +285,7 @@ bool silc_server_init(SilcServer server)
   server->starttime = time(NULL);
 
   /* Take config object for us */
-  silc_server_config_ref(&server->config_ref, server->config, 
+  silc_server_config_ref(&server->config_ref, server->config,
                         server->config);
 
   /* Steal public and private key from the config object */
@@ -257,7 +320,8 @@ bool silc_server_init(SilcServer server)
   silc_pkcs_private_key_set(server->pkcs, server->private_key);
 
   /* Initialize the scheduler */
-  server->schedule = silc_schedule_init(server->config->param.connections_max);
+  server->schedule = silc_schedule_init(server->config->param.connections_max,
+                                       server);
   if (!server->schedule)
     goto err;
 
@@ -279,7 +343,7 @@ bool silc_server_init(SilcServer server)
   server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 
   /* Init watcher list */
-  server->watcher_list = 
+  server->watcher_list =
     silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
                          silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
                          NULL, NULL, TRUE);
@@ -411,27 +475,23 @@ bool silc_server_init(SilcServer server)
   /* Clients local list */
   server->purge_i = purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->local_list->clients;
-  purge->schedule = server->schedule;
   purge->timeout = 600;
-  silc_schedule_task_add(purge->schedule, 0,
-                        silc_idlist_purge,
+  silc_schedule_task_add(server->schedule, 0, silc_idlist_purge,
                         (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   /* Clients global list */
   server->purge_g = purge = silc_calloc(1, sizeof(*purge));
   purge->cache = server->global_list->clients;
-  purge->schedule = server->schedule;
   purge->timeout = 300;
-  silc_schedule_task_add(purge->schedule, 0,
-                        silc_idlist_purge,
+  silc_schedule_task_add(server->schedule, 0, silc_idlist_purge,
                         (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   /* If we are normal server we'll retrieve network statisticial information
      once in a while from the router. */
   if (server->server_type == SILC_SERVER)
-    silc_schedule_task_add(purge->schedule, 0, silc_server_get_stats,
+    silc_schedule_task_add(server->schedule, 0, silc_server_get_stats,
                           server, 10, 0, SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_LOW);
 
@@ -459,7 +519,7 @@ SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
   if (!sock)
     return;
 
-  SILC_LOG_INFO(("Closing connection %s:%d [%s]: connection is unconfigured",
+  SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
                 sock->hostname, sock->port,
                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
@@ -555,14 +615,15 @@ bool silc_server_rehash(SilcServer server)
 
       /* Check whether new config has this one too */
       for (newptr = newconfig->routers; newptr; newptr = newptr->next) {
-       if (!strcmp(newptr->host, ptr->host) && newptr->port == ptr->port &&
+       if (silc_string_compare(newptr->host, ptr->host) && 
+           newptr->port == ptr->port &&
            newptr->initiator == ptr->initiator) {
          found = TRUE;
          break;
        }
       }
 
-      if (!found) {
+      if (!found && ptr->host) {
        /* Remove this connection */
        SilcSocketConnection sock;
        sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
@@ -586,13 +647,13 @@ bool silc_server_rehash(SilcServer server)
 
       /* Check whether new config has this one too */
       for (newptr = newconfig->servers; newptr; newptr = newptr->next) {
-       if (!strcmp(newptr->host, ptr->host)) {
+       if (silc_string_compare(newptr->host, ptr->host)) {
          found = TRUE;
          break;
        }
       }
 
-      if (!found) {
+      if (!found && ptr->host) {
        /* Remove this connection */
        SilcSocketConnection sock;
        sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_SERVER,
@@ -606,6 +667,36 @@ bool silc_server_rehash(SilcServer server)
     }
   }
 
+  if (server->config->clients) {
+    SilcServerConfigClient *ptr;
+    SilcServerConfigClient *newptr;
+    bool found;
+
+    for (ptr = server->config->clients; ptr; ptr = ptr->next) {
+      found = FALSE;
+
+      /* Check whether new config has this one too */
+      for (newptr = newconfig->clients; newptr; newptr = newptr->next) {
+       if (silc_string_compare(newptr->host, ptr->host)) {
+         found = TRUE;
+         break;
+       }
+      }
+
+      if (!found && ptr->host) {
+       /* Remove this connection */
+       SilcSocketConnection sock;
+       sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_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);
+      }
+    }
+  }
+
   /* Go through all configured routers after rehash */
   silc_schedule_task_add(server->schedule, 0,
                         silc_server_connect_to_router,
@@ -663,7 +754,7 @@ void silc_server_run(SilcServer server)
 
 void silc_server_stop(SilcServer server)
 {
-  SILC_LOG_DEBUG(("Stopping server"));
+  SILC_LOG_INFO(("SILC Server shutting down"));
 
   if (server->schedule) {
     int i;
@@ -675,7 +766,8 @@ void silc_server_stop(SilcServer server)
       if (!server->sockets[i])
        continue;
       if (!SILC_IS_LISTENER(server->sockets[i])) {
-       SilcIDListData idata = server->sockets[i]->user_data;
+       SilcSocketConnection sock = server->sockets[i];
+       SilcIDListData idata = sock->user_data;
 
        if (idata)
          idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
@@ -685,6 +777,10 @@ void silc_server_stop(SilcServer server)
        silc_server_disconnect_remote(server, server->sockets[i], 
                                      SILC_STATUS_OK, 
                                      "Server is shutting down");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock,
+                                         "Server is shutting down");
+       silc_socket_free(sock);
       } else {
        silc_socket_free(server->sockets[i]);
        server->sockets[i] = NULL;
@@ -792,12 +888,21 @@ void silc_server_start_key_exchange(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
 {
+  SilcServer server = app_context;
   SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
   SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
   SilcServerConfigConnParams *param =
                (conn->param ? conn->param : &server->config->param);
 
+  /* Don't retry if we are shutting down. */
+  if (server->server_shutdown) {
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+
   SILC_LOG_INFO(("Retrying connecting to a router"));
 
   /* Calculate next timeout */
@@ -839,11 +944,19 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
 
 SILC_TASK_CALLBACK(silc_server_connect_router)
 {
+  SilcServer server = app_context;
   SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcServer server = sconn->server;
   SilcServerConfigRouter *rconn;
   int sock;
 
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown) {
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+
   SILC_LOG_INFO(("Connecting to the %s %s on port %d",
                 (sconn->backup ? "backup router" : "router"),
                 sconn->remote_host, sconn->remote_port));
@@ -895,6 +1008,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   SilcServerConnection sconn;
   SilcServerConfigRouter *ptr;
 
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown)
+    return;
+
   SILC_LOG_DEBUG(("We are %s",
                  (server->server_type == SILC_SERVER ?
                   "normal server" : server->server_type == SILC_ROUTER ?
@@ -923,7 +1040,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
                    ptr->host, ptr->port));
 
     if (server->server_type == SILC_ROUTER && ptr->backup_router &&
-       ptr->initiator == FALSE && !server->backup_router)
+       ptr->initiator == FALSE && !server->backup_router &&
+       !silc_server_config_get_backup_router(server))
       server->wait_backup = TRUE;
 
     if (ptr->initiator) {
@@ -940,7 +1058,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
-      sconn->server = server;
       sconn->remote_host = strdup(ptr->host);
       sconn->remote_port = ptr->port;
       sconn->backup = ptr->backup_router;
@@ -1158,7 +1275,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcSocketConnection sock = ctx->sock;
   SilcServerEntry id_entry = NULL;
   SilcBuffer packet;
-  SilcServerHBContext hb_context;
   unsigned char *id_string;
   SilcUInt32 id_len;
   SilcIDListData idata;
@@ -1262,15 +1378,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Perform keepalive. The `hb_context' will be freed automatically
      when finally calling the silc_socket_free function. */
-  hb_context = silc_calloc(1, sizeof(*hb_context));
-  hb_context->server = server;
-  silc_socket_set_heartbeat(sock, param->keepalive_secs, hb_context,
+  silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
                            silc_server_perform_heartbeat,
                            server->schedule);
 
   /* Register re-key timeout */
   idata->rekey->timeout = param->key_exchange_rekey;
-  idata->rekey->context = (void *)server;
   silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_rekey_callback,
                         (void *)sock, idata->rekey->timeout, 0,
@@ -1279,6 +1392,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   if (!sconn->backup) {
     /* Mark this router our primary router if we're still standalone */
     if (server->standalone) {
+      SILC_LOG_DEBUG(("This connection is our primary router"));
       server->id_entry->router = id_entry;
       server->router = id_entry;
       server->standalone = FALSE;
@@ -1297,7 +1411,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
       /* 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, 0, TRUE);
+       silc_server_backup_add(server, server->id_entry, sock->ip,
+                              sconn->remote_port, TRUE);
     }
   } else {
     /* Add this server to be our backup router */
@@ -1310,7 +1425,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
  out:
   /* Call the completion callback to indicate that we've connected to
      the router */
-  if (sconn->callback)
+  if (sconn && sconn->callback)
     (*sconn->callback)(server, id_entry, sconn->callback_context);
 
   /* Free the temporary connection data context */
@@ -1355,8 +1470,6 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   SilcServerConfigDeny *deny;
   int port;
 
-  context = (void *)server;
-
   /* Check whether we could resolve both IP and FQDN. */
   if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
                    server->config->require_reverse_lookup)) {
@@ -1377,6 +1490,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
      However, this doesn't set the scheduler for outgoing traffic, it
      will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
      later when outgoing data is available. */
+  context = (void *)server;
   SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
 
   SILC_LOG_INFO(("Incoming connection %s (%s)", sock->hostname,
@@ -1659,7 +1773,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
-  SilcServerHBContext hb_context;
   SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
   void *id_entry;
   SilcUInt32 hearbeat_timeout = server->config->param.keepalive_secs;
@@ -1761,6 +1874,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       if (conn->param) {
        if (conn->param->keepalive_secs)
          hearbeat_timeout = conn->param->keepalive_secs;
+
+       /* Check if to be anonymous connection */
+       if (conn->param->anonymous)
+         client->mode |= SILC_UMODE_ANONYMOUS;
       }
 
       id_entry = (void *)client;
@@ -1968,9 +2085,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
   /* Perform keepalive. The `hb_context' will be freed automatically
      when finally calling the silc_socket_free function. */
-  hb_context = silc_calloc(1, sizeof(*hb_context));
-  hb_context->server = server;
-  silc_socket_set_heartbeat(sock, hearbeat_timeout, hb_context,
+  silc_socket_set_heartbeat(sock, hearbeat_timeout, server,
                            silc_server_perform_heartbeat,
                            server->schedule);
 
@@ -2119,14 +2234,13 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
                                    TRUE : FALSE, cipher, hmac, sequence,
                                    silc_server_packet_parse, server);
 
-  /* If this socket connection is not authenticated yet and the packet
-     processing failed we will drop the connection since it can be
-     a malicious flooder. */
-  if (sock->type == SILC_SOCKET_TYPE_UNKNOWN && ret == FALSE &&
-      (!sock->protocol || sock->protocol->protocol->type ==
-       SILC_PROTOCOL_SERVER_KEY_EXCHANGE)) {
-    SILC_LOG_DEBUG(("Bad data sent from unknown connection %d", sock->sock));
-    SILC_SET_DISCONNECTING(sock);
+  /* If processing failed the connection is closed. */
+  if (!ret) {
+    /* On packet processing errors we may close our primary router 
+       connection but won't become primary router if we are the backup
+       since this is local error condition. */
+    if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
+      server->backup_noswitch = TRUE;
 
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock, NULL);
@@ -2154,13 +2268,9 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
 
   /* If entry is disabled ignore what we got. */
-  if (ret != SILC_PACKET_RESUME_ROUTER &&
-      idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
-    SILC_LOG_DEBUG(("Connection is disabled"));
-    goto out;
-  }
-  if (ret != SILC_PACKET_HEARTBEAT &&
-      idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED && 
+      ret != SILC_PACKET_HEARTBEAT && ret != SILC_PACKET_RESUME_ROUTER &&
+      ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE) {
     SILC_LOG_DEBUG(("Connection is disabled"));
     goto out;
   }
@@ -2237,6 +2347,7 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = parser_context->sock;
   SilcIDListData idata = (SilcIDListData)sock->user_data;
+  bool ret;
 
   if (idata)
     idata->psn_receive = parser_context->packet->sequence + 1;
@@ -2250,21 +2361,36 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
       (sock->protocol && sock->protocol->protocol &&
        (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
-    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+    silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
 
     /* Reprocess data since we'll return FALSE here.  This is because
        the idata->receive_key might have become valid in the last packet
        and we want to call this processor with valid cipher. */
     if (idata)
-      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+      ret = silc_packet_receive_process(
+                                 sock, server->server_type == SILC_ROUTER ?
                                  TRUE : FALSE, idata->receive_key,
                                  idata->hmac_receive, idata->psn_receive,
                                  silc_server_packet_parse, server);
     else
-      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+      ret = silc_packet_receive_process(
+                                 sock, server->server_type == SILC_ROUTER ?
                                  TRUE : FALSE, NULL, NULL, 0,
                                  silc_server_packet_parse, server);
+
+    if (!ret) {
+      /* On packet processing errors we may close our primary router 
+         connection but won't become primary router if we are the backup
+         since this is local error condition. */
+      if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
+       server->backup_noswitch = TRUE;
+
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_server_close_connection(server, sock);
+    }
+
     return FALSE;
   }
 
@@ -2281,7 +2407,7 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     /* Packets from servers are parsed immediately */
-    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+    silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
     break;
   default:
@@ -2358,13 +2484,12 @@ void silc_server_packet_parse_type(SilcServer server,
     if (sock->protocol) {
       SilcServerFailureContext f;
       f = silc_calloc(1, sizeof(*f));
-      f->server = server;
-      f->sock = sock;
+      f->sock = silc_socket_dup(sock);
 
       /* We will wait 5 seconds to process this failure packet */
       silc_schedule_task_add(server->schedule, sock->sock,
-                        silc_server_failure_callback, (void *)f, 5, 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+                            silc_server_failure_callback, (void *)f, 5, 0,
+                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     }
     break;
 
@@ -2740,7 +2865,6 @@ void silc_server_create_connection(SilcServer server,
 
   /* Allocate connection object for hold connection specific stuff. */
   sconn = silc_calloc(1, sizeof(*sconn));
-  sconn->server = server;
   sconn->remote_host = strdup(remote_host);
   sconn->remote_port = port;
   sconn->no_reconnect = TRUE;
@@ -2761,6 +2885,8 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final)
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
+  char tmp[128];
+
   if (!server->sockets[sock->sock] && SILC_IS_DISCONNECTED(sock)) {
     silc_schedule_task_add(server->schedule, 0,
                           silc_server_close_connection_final,
@@ -2769,12 +2895,14 @@ void silc_server_close_connection(SilcServer server,
     return;
   }
 
-  SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
+  memset(tmp, 0, sizeof(tmp));
+  silc_socket_get_error(sock, tmp, sizeof(tmp));
+  SILC_LOG_INFO(("Closing connection %s:%d [%s] %s", sock->hostname,
                   sock->port,
                   (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                    sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
                    sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                   "Router")));
+                   "Router"), tmp[0] ? tmp : ""));
 
   /* We won't listen for this connection anymore */
   silc_schedule_unset_listen_fd(server->schedule, sock->sock);
@@ -2821,7 +2949,7 @@ void silc_server_disconnect_remote(SilcServer server,
   char *cp;
   int len;
 
-  if (!sock)
+  if (!sock || SILC_IS_DISCONNECTED(sock))
     return;
 
   memset(buf, 0, sizeof(buf));
@@ -2862,18 +2990,15 @@ void silc_server_disconnect_remote(SilcServer server,
   silc_server_close_connection(server, sock);
 }
 
-typedef struct {
-  SilcServer server;
-  SilcClientEntry client;
-} *FreeClientInternal;
-
 SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
 {
-  FreeClientInternal i = (FreeClientInternal)context;
+  SilcServer server = app_context;
+  SilcClientEntry client = context;
+
+  assert(!silc_hash_table_count(client->channels));
 
-  silc_idlist_del_data(i->client);
-  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
-  silc_free(i);
+  silc_idlist_del_data(client);
+  silc_idcache_purge_by_context(server->local_list->clients, client);
 }
 
 /* Frees client data and notifies about client's signoff. */
@@ -2884,20 +3009,8 @@ void silc_server_free_client_data(SilcServer server,
                                  int notify,
                                  const char *signoff)
 {
-  FreeClientInternal i = silc_calloc(1, sizeof(*i));
-
   SILC_LOG_DEBUG(("Freeing client data"));
 
-#if 1
-  if (!client->router && !client->connection &&
-      !(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-    SILC_LOG_ERROR(("****** freeing data for already unregistered client -s"));
-    SILC_LOG_ERROR(("****** Contact Pekka"));
-    SILC_LOG_ERROR(("****** freeing data for already unregistered client -e"));
-    return;
-  }
-#endif
-
   /* If there is pending outgoing data for the client then purge it
      to the network before removing the client entry. */
   silc_server_packet_queue_purge(server, sock);
@@ -2936,17 +3049,23 @@ void silc_server_free_client_data(SilcServer server,
   silc_schedule_task_del_by_context(server->schedule, client);
 
   /* We will not delete the client entry right away. We will take it
-     into history (for WHOWAS command) for 5 minutes */
-  i->server = server;
-  i->client = client;
-  silc_schedule_task_add(server->schedule, 0,
-                        silc_server_free_client_data_timeout,
-                        (void *)i, 300, 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-  client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
-  client->mode = 0;
-  client->router = NULL;
-  client->connection = NULL;
+     into history (for WHOWAS command) for 5 minutes, unless we're
+     shutting down server. */
+  if (!server->server_shutdown) {
+    silc_schedule_task_add(server->schedule, 0,
+                          silc_server_free_client_data_timeout,
+                          client, 300, 0,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
+    client->mode = 0;
+    client->router = NULL;
+    client->connection = NULL;
+  } else {
+    /* Delete directly since we're shutting down server */
+    silc_idlist_del_data(client);
+    silc_idlist_del_client(server->local_list, client);
+  }
 }
 
 /* Frees user_data pointer from socket connection object. This also sends
@@ -2976,6 +3095,14 @@ void silc_server_free_sock_user_data(SilcServer server,
       if (user_data->id)
        backup_router = silc_server_backup_get(server, user_data->id);
 
+      if (!server->backup_router && server->server_type == SILC_ROUTER &&
+         backup_router == server->id_entry &&
+         sock->type != SILC_SOCKET_TYPE_ROUTER)
+       backup_router = NULL;
+
+      if (server->server_shutdown || server->backup_noswitch)
+       backup_router = NULL;
+
       /* If this was our primary router connection then we're lost to
         the outside world. */
       if (server->router == user_data) {
@@ -3032,6 +3159,14 @@ void silc_server_free_sock_user_data(SilcServer server,
        /* Mark this connection as replaced */
        silc_server_backup_replaced_add(server, user_data->id,
                                        backup_router);
+      } else if (server->server_type == SILC_SERVER &&
+                sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       /* Reconnect to the router (backup) */
+       silc_schedule_task_add(server->schedule, 0,
+                              silc_server_connect_to_router,
+                              server, 1, 0,
+                              SILC_TASK_TIMEOUT,
+                              SILC_TASK_PRI_NORMAL);
       }
 
       if (!backup_router) {
@@ -3040,9 +3175,16 @@ void silc_server_free_sock_user_data(SilcServer server,
        silc_server_remove_servers_by_server(server, user_data, TRUE);
 
        /* Remove the clients that this server owns as they will become
-          invalid now too. */
-       silc_server_remove_clients_by_server(server, user_data,
-                                            user_data, TRUE);
+          invalid now too.  For backup router the server is actually
+          coming from the primary router, so mark that as the owner
+          of this entry. */
+       if (server->server_type == SILC_BACKUP_ROUTER &&
+           sock->type == SILC_SOCKET_TYPE_SERVER)
+         silc_server_remove_clients_by_server(server, server->router,
+                                              user_data, TRUE);
+       else
+         silc_server_remove_clients_by_server(server, user_data,
+                                              user_data, TRUE);
 
        /* Remove channels owned by this server */
        if (server->server_type == SILC_SERVER)
@@ -3091,6 +3233,7 @@ void silc_server_free_sock_user_data(SilcServer server,
                                  server->server_name,
                                  server->router->server_name));
       }
+      server->backup_noswitch = FALSE;
 
       /* Free the server entry */
       silc_server_backup_del(server, user_data);
@@ -3192,7 +3335,7 @@ void silc_server_remove_from_channels(SilcServer server,
     }
 
     silc_hash_table_del(client->channels, channel);
-    silc_hash_table_del(channel->user_list, chl->client);
+    silc_hash_table_del(channel->user_list, client);
     channel->user_count--;
 
     /* If there is no global users on the channel anymore mark the channel
@@ -3201,6 +3344,7 @@ void silc_server_remove_from_channels(SilcServer server,
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
+    memset(chl, 'A', sizeof(*chl));
     silc_free(chl);
 
     /* Update statistics */
@@ -3293,8 +3437,8 @@ bool silc_server_remove_from_one_channel(SilcServer server,
     return FALSE;
   }
 
-  silc_hash_table_del(client->channels, chl->channel);
-  silc_hash_table_del(channel->user_list, chl->client);
+  silc_hash_table_del(client->channels, channel);
+  silc_hash_table_del(channel->user_list, client);
   channel->user_count--;
 
   /* If there is no global users on the channel anymore mark the channel
@@ -3303,6 +3447,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
+  memset(chl, 'O', sizeof(*chl));
   silc_free(chl);
 
   /* Update statistics */
@@ -3373,15 +3518,15 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
     return;
   }
 
-  if (sock->user_data)
-    silc_server_free_sock_user_data(server, sock, NULL);
-
   silc_server_disconnect_remote(server, sock, 
                                protocol == 
                                SILC_PROTOCOL_SERVER_CONNECTION_AUTH ?
                                SILC_STATUS_ERR_AUTH_FAILED :
                                SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
                                "Connection timeout");
+
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock, NULL);
 }
 
 /* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
@@ -3574,8 +3719,8 @@ silc_server_create_new_channel_with_id(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
 {
+  SilcServer server = app_context;
   SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
-  SilcServer server = (SilcServer)rekey->context;
 
   rekey->task = NULL;
 
@@ -3649,7 +3794,6 @@ bool silc_server_create_channel_key(SilcServer server,
   if (server->server_type == SILC_ROUTER) {
     if (!channel->rekey)
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
-    channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
     channel->rekey->key_len = key_len;
     if (channel->rekey->task)
@@ -3705,8 +3849,9 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     if (!channel) {
       channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
       if (!channel) {
-       SILC_LOG_ERROR(("Received key for non-existent channel %s",
-                       silc_id_render(id, SILC_ID_CHANNEL)));
+       if (server->server_type == SILC_ROUTER)
+         SILC_LOG_ERROR(("Received key for non-existent channel %s",
+                         silc_id_render(id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -3762,7 +3907,6 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   if (server->server_type == SILC_ROUTER) {
     if (!channel->rekey)
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
-    channel->rekey->context = (void *)server;
     channel->rekey->channel = channel;
     if (channel->rekey->task)
       silc_schedule_task_del(server->schedule, channel->rekey->task);
@@ -3791,12 +3935,13 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 void silc_server_perform_heartbeat(SilcSocketConnection sock,
                                   void *hb_context)
 {
-  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
+  SilcServer server = hb_context;
 
-  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
+  SILC_LOG_DEBUG(("Sending heartbeat to %s:%d (%s)", sock->hostname, 
+                sock->port, sock->ip));
 
   /* Send the heartbeat */
-  silc_server_send_heartbeat(hb->server, sock);
+  silc_server_send_heartbeat(server, sock);
 }
 
 /* Returns assembled of all servers in the given ID list. The packet's
@@ -3926,6 +4071,12 @@ static void silc_server_announce_get_clients(SilcServer server,
            break;
          continue;
        }
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) &&
+           !client->connection && !client->router && !SILC_IS_LOCAL(client)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
 
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -4380,13 +4531,15 @@ void silc_server_announce_channels(SilcServer server,
 
 SILC_TASK_CALLBACK(silc_server_failure_callback)
 {
+  SilcServer server = app_context;
   SilcServerFailureContext f = (SilcServerFailureContext)context;
 
   if (f->sock->protocol) {
     f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-    silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
+    silc_protocol_execute(f->sock->protocol, server->schedule, 0, 0);
   }
 
+  silc_socket_free(f->sock);
   silc_free(f);
 }
 
@@ -4462,9 +4615,9 @@ void silc_server_save_users_on_channel(SilcServer server,
   SilcClientEntry client;
   SilcIDCacheEntry cache;
   SilcChannelClientEntry chl;
-  bool global;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Saving %d users on %s channel", user_count,
+                 channel->channel_name));
 
   for (i = 0; i < user_count; i++) {
     /* Client ID */
@@ -4484,21 +4637,19 @@ void silc_server_save_users_on_channel(SilcServer server,
       continue;
     }
 
-    global = FALSE;
+    cache = NULL;
 
     /* Check if we have this client cached already. */
     client = silc_idlist_find_client_by_id(server->local_list, client_id,
                                           server->server_type, &cache);
-    if (!client) {
+    if (!client)
       client = silc_idlist_find_client_by_id(server->global_list,
                                             client_id, server->server_type,
                                             &cache);
-      global = TRUE;
-    }
     if (!client) {
       /* If router did not find such Client ID in its lists then this must
         be bogus client or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER) {
+      if (server->server_type != SILC_SERVER) {
        silc_free(client_id);
        continue;
       }
@@ -4516,15 +4667,18 @@ void silc_server_save_users_on_channel(SilcServer server,
       }
 
       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
-    } else {
-      /* Found, if it is from global list we'll assure that we won't
-        expire it now that the entry is on channel. */
-      if (global)
-       cache->expire = 0;
     }
 
+    if (cache)
+      cache->expire = 0;
     silc_free(client_id);
 
+    if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      SILC_LOG_ERROR(("Attempting to add unregistered client to channel ",
+                     "%s", channel->channel_name));
+      continue;
+    }
+
     if (!silc_server_client_on_channel(client, channel, &chl)) {
       /* Client was not on the channel, add it. */
       chl = silc_calloc(1, sizeof(*chl));
@@ -4562,7 +4716,8 @@ void silc_server_save_user_channels(SilcServer server,
   char *name;
   int i = 0;
 
-  if (!channels ||!channels_user_modes)
+  if (!channels || !channels_user_modes ||
+      !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
     goto out;
   
   ch = silc_channel_payload_parse_list(channels->data, channels->len);
@@ -4807,69 +4962,14 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   return buffer;
 }
 
-/* Finds client entry by Client ID and if it is not found then resolves
-   it using WHOIS command. */
-
-SilcClientEntry silc_server_get_client_resolve(SilcServer server,
-                                              SilcClientID *client_id,
-                                              bool always_resolve,
-                                              bool *resolved)
-{
-  SilcClientEntry client;
-
-  if (resolved)
-    *resolved = FALSE;
-
-  client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                        TRUE, NULL);
-  if (!client) {
-    client = silc_idlist_find_client_by_id(server->global_list,
-                                          client_id, TRUE, NULL);
-    if (!client && server->server_type == SILC_ROUTER)
-      return NULL;
-  }
-
-  if (!client && server->standalone)
-    return NULL;
-
-  if (!client || !client->nickname || !client->username ||
-      always_resolve) {
-    SilcBuffer buffer, idp;
-
-    if (client) {
-      client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
-      client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
-      client->resolve_cmd_ident = ++server->cmd_ident;
-    }
-
-    idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
-                                           server->cmd_ident, 1,
-                                           4, idp->data, idp->len);
-    silc_server_packet_send(server, client ? client->router->connection :
-                           SILC_PRIMARY_ROUTE(server),
-                           SILC_PACKET_COMMAND, 0,
-                           buffer->data, buffer->len, FALSE);
-    silc_buffer_free(idp);
-    silc_buffer_free(buffer);
-
-    if (resolved)
-      *resolved = TRUE;
-
-    return NULL;
-  }
-
-  return client;
-}
-
 /* A timeout callback for the re-key. We will be the initiator of the
    re-key protocol. */
 
 SILC_TASK_CALLBACK(silc_server_rekey_callback)
 {
+  SilcServer server = app_context;
   SilcSocketConnection sock = (SilcSocketConnection)context;
   SilcIDListData idata = (SilcIDListData)sock->user_data;
-  SilcServer server = (SilcServer)idata->rekey->context;
   SilcProtocol protocol;
   SilcServerRekeyInternalContext *proto_ctx;