New silcconfig library and server parser. Merged silc-newconfig-final.patch.
[silc.git] / apps / silcd / server.c
index 86b0a3dc048bb11b5ff537dd8cbbd69855c388d9..f0ed13f8e6b1950302bfdb8045d0f1e150ffcdb1 100644 (file)
@@ -115,21 +115,20 @@ int silc_server_init(SilcServer server)
   SilcServerID *id;
   SilcServerEntry id_entry;
   SilcIDListPurge purge;
-  SilcServerConfigSectionListenPort *listen;
 
   SILC_LOG_DEBUG(("Initializing server"));
   assert(server);
   assert(server->config);
 
   /* Set public and private keys */
-  if (!server->config->server_keys ||
-      !server->config->server_keys->public_key || 
-      !server->config->server_keys->private_key) {
+  if (!server->config->server_info ||
+      !server->config->server_info->public_key || 
+      !server->config->server_info->private_key) {
     SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
     return FALSE;
   }
-  server->public_key = server->config->server_keys->public_key;
-  server->private_key = server->config->server_keys->private_key;
+  server->public_key = server->config->server_info->public_key;
+  server->private_key = server->config->server_info->private_key;
 
   /* XXX After server is made as Silc Server Library this can be given
      as argument, for now this is hard coded */
@@ -141,18 +140,14 @@ int silc_server_init(SilcServer server)
   server->params->protocol_timeout = 60;
   server->params->require_reverse_mapping = FALSE;
 
-  /* Set log files where log message should be saved. */
-  server->config->server = server;
-  silc_server_config_setlogfiles(server->config);
   /* Register all configured ciphers, PKCS and hash functions. */
-  if (!silc_server_config_register_ciphers(server->config))
+  if (!silc_server_config_register_ciphers(server))
     silc_cipher_register_default();
-  if (!silc_server_config_register_pkcs(server->config))
+  if (!silc_server_config_register_pkcs(server))
     silc_pkcs_register_default();
-  if (!silc_server_config_register_hashfuncs(server->config))
+  if (!silc_server_config_register_hashfuncs(server))
     silc_hash_register_default();
-  if (!silc_server_config_register_hmacs(server->config))
+  if (!silc_server_config_register_hmacs(server))
     silc_hmac_register_default();
 
   /* Initialize random number generator for the server. */
@@ -164,9 +159,6 @@ int silc_server_init(SilcServer server)
   silc_hash_alloc("md5", &server->md5hash);
   silc_hash_alloc("sha1", &server->sha1hash);
 
-  /* Initialize none cipher */
-  silc_cipher_alloc("none", &server->none_cipher);
-
   /* Allocate PKCS context for local public and private keys */
   silc_pkcs_alloc(server->public_key->name, &server->pkcs);
   silc_pkcs_public_key_set(server->pkcs, server->public_key);
@@ -175,24 +167,23 @@ int silc_server_init(SilcServer server)
   /* Create a listening server. Note that our server can listen on multiple
      ports. All listeners are created here and now. */
   sock_count = 0;
-  listen = server->config->listen_port;
-  while(listen) {
+  while (1) {
     int tmp;
 
-    tmp = silc_net_create_server(server->config->listen_port->port,
-                                server->config->listen_port->listener_ip);
+    tmp = silc_net_create_server(server->config->server_info->port,
+                                server->config->server_info->server_ip);
 
     if (tmp < 0) {
-      SILC_LOG_ERROR(("Could not create server listener: %s on %d",
-                     server->config->listen_port->listener_ip,
-                     server->config->listen_port->port));
+      SILC_LOG_ERROR(("Could not create server listener: %s on %hd",
+                     server->config->server_info->server_ip,
+                     server->config->server_info->port));
       goto err0;
     }
 
     sock = silc_realloc(sock, sizeof(*sock) * (sock_count + 1));
     sock[sock_count] = tmp;
     sock_count++;
-    listen = listen->next;
+    break;
   }
 
   /* Initialize ID caches */
@@ -284,7 +275,7 @@ int silc_server_init(SilcServer server)
   server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
   if (!server->schedule)
     goto err0;
-  
+
   /* Add the first task to the scheduler. This is task that is executed by
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
@@ -305,10 +296,13 @@ int silc_server_init(SilcServer server)
                         SILC_TASK_PRI_NORMAL);
   server->listenning = TRUE;
 
+  /* Send log file configuration */
+  silc_server_config_setlogfiles(server->config, server->schedule);
+
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
   if (server->config->servers) {
-    SilcServerConfigSectionServerConnection *ptr = server->config->servers;
+    SilcServerConfigSectionServer *ptr = server->config->servers;
 
     server->server_type = SILC_ROUTER;
     while (ptr) {
@@ -329,18 +323,20 @@ int silc_server_init(SilcServer server)
   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,
-                        (void *)purge, 600, 0,
+                        (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   /* Clients global list */
   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,
-                        (void *)purge, 300, 0,
+                        (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   SILC_LOG_DEBUG(("Server initialized"));
@@ -355,27 +351,26 @@ int silc_server_init(SilcServer server)
   return FALSE;
 }
 
-/* Fork server to background and set gid+uid to non-root */
+/* Fork server to background */
 
 void silc_server_daemonise(SilcServer server)
 {
   int i;
 
-  i = fork ();
+  SILC_LOG_DEBUG(("Forking SILC server to background"));
 
-  if (i) {
-    if (i > 0) {
-      if (geteuid())
-        SILC_LOG_DEBUG(("Server started as user"));
-      else
-        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+  i = fork();
 
-      SILC_LOG_DEBUG(("Forking SILC server to background"));
-      exit(0);
-    } else {
-      SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
-      exit(1);
-    }
+  if (i < 0) {
+    SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
+    exit(1);
+  }
+  else if (i) {
+    if (geteuid())
+      SILC_LOG_DEBUG(("Server started as user"));
+    else
+      SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+    exit(0);
   }
   setsid();
 }
@@ -386,13 +381,11 @@ void silc_server_drop(SilcServer server)
 {
   /* Are we executing silcd as root or a regular user? */
   if (!geteuid()) {
-
     struct passwd *pw;
     struct group *gr;
     char *user, *group;
 
-    if (!server->config->identity || !server->config->identity->user || 
-       !server->config->identity->group) {
+    if (!server->config->server_info->user || !server->config->server_info->group) {
       fprintf(stderr, "Error:"
        "\tSILC server must not be run as root.  For the security of your\n"
        "\tsystem it is strongly suggested that you run SILC under dedicated\n"
@@ -402,8 +395,8 @@ void silc_server_drop(SilcServer server)
     }
 
     /* Get the values given for user and group in configuration file */
-    user=server->config->identity->user;
-    group=server->config->identity->group;
+    user=server->config->server_info->user;
+    group=server->config->server_info->group;
 
     /* Check whether the user/group information is text */ 
     if (atoi(user)!=0 || atoi(group)!=0) {
@@ -499,8 +492,11 @@ void silc_server_stop(SilcServer server)
 {
   SILC_LOG_DEBUG(("Stopping server"));
 
-  silc_schedule_stop(server->schedule);
-  silc_schedule_uninit(server->schedule);
+  if (server->schedule) {
+    silc_schedule_stop(server->schedule);
+    silc_schedule_uninit(server->schedule);
+    server->schedule = NULL;
+  }
 
   silc_server_protocols_unregister();
 
@@ -520,6 +516,10 @@ void silc_server_start_key_exchange(SilcServer server,
   SilcServerKEInternalContext *proto_ctx;
   void *context;
 
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+
   /* Set socket options */
   silc_net_set_socket_nonblock(sock);
   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
@@ -602,6 +602,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
   if (sconn->retry_count > server->params->retry_count && 
       server->params->retry_keep_trying == FALSE) {
     SILC_LOG_ERROR(("Could not connect to router, giving up"));
+    silc_free(sconn->remote_host);
+    silc_free(sconn);
     return;
   }
 
@@ -627,15 +629,17 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   server->router_connect = time(0);
 
   /* Connect to remote host */
-  sock = silc_net_create_connection(server->config->listen_port->local_ip,
+  sock = silc_net_create_connection(server->config->server_info->server_ip,
                                    sconn->remote_port, 
                                    sconn->remote_host);
   if (sock < 0) {
-    SILC_LOG_ERROR(("Could not connect to router"));
-    silc_schedule_task_add(server->schedule, fd, 
-                          silc_server_connect_to_router_retry,
-                          context, 0, 1, SILC_TASK_TIMEOUT, 
-                          SILC_TASK_PRI_NORMAL);
+    SILC_LOG_ERROR(("Could not connect to router %s:%d",
+                   sconn->remote_host, sconn->remote_port));
+    if (!sconn->no_reconnect)
+      silc_schedule_task_add(server->schedule, fd, 
+                            silc_server_connect_to_router_retry,
+                            context, 0, 1, SILC_TASK_TIMEOUT, 
+                            SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -652,7 +656,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
-  SilcServerConfigSectionServerConnection *ptr;
+  SilcServerConfigSectionRouter *ptr;
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
@@ -685,6 +689,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
        sconn->backup_replace_port = ptr->backup_replace_port;
       }
 
+      if (!server->router_conn && !sconn->backup)
+       server->router_conn = sconn;
+
       silc_schedule_task_add(server->schedule, fd, 
                             silc_server_connect_router,
                             (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
@@ -716,7 +723,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   SilcServerConnection sconn = (SilcServerConnection)ctx->context;
   SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
-  SilcServerConfigSectionServerConnection *conn = NULL;
+  SilcServerConfigSectionRouter *conn = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -936,7 +943,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
      timeout!! */
   hb_context = silc_calloc(1, sizeof(*hb_context));
   hb_context->server = server;
-  silc_socket_set_heartbeat(sock, 600, hb_context,
+  silc_socket_set_heartbeat(sock, 300, hb_context,
                            silc_server_perform_heartbeat,
                            server->schedule);
 
@@ -984,6 +991,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
     silc_free(sconn->backup_replace_ip);
     silc_free(sconn);
   }
+  if (sconn == server->router_conn)
+    server->router_conn = NULL;
 
   /* Free the protocol object */
   if (sock->protocol == protocol)
@@ -993,6 +1002,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  silc_free(ctx->auth_data);
   silc_free(ctx);
 }
 
@@ -1008,7 +1018,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   SilcServer server = (SilcServer)context;
   SilcServerKEInternalContext *proto_ctx;
   void *cconfig, *sconfig, *rconfig;
-  SilcServerConfigSectionDenyConnection *deny;
+  SilcServerConfigSectionDeny *deny;
   int port;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1039,16 +1049,16 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   port = server->sockets[server->sock]->port; /* Listenning port */
 
   /* Check whether this connection is denied to connect to us. */
-  deny = silc_server_config_denied_conn(server->config, sock->ip, port);
+  deny = silc_server_config_find_denied(server->config, sock->ip, port);
   if (!deny)
-    deny = silc_server_config_denied_conn(server->config, sock->hostname,
+    deny = silc_server_config_find_denied(server->config, sock->hostname,
                                          port);
   if (deny) {
     /* The connection is denied */
     SILC_LOG_INFO(("Connection %s (%s) is denied", 
                    sock->hostname, sock->ip));
-    silc_server_disconnect_remote(server, sock, deny->comment ?
-                                 deny->comment :
+    silc_server_disconnect_remote(server, sock, deny->reason ?
+                                 deny->reason :
                                  "Server closed connection: "
                                  "Connection refused");
     server->stat.conn_failures++;
@@ -1058,9 +1068,9 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   /* Check whether we have configred this sort of connection at all. We
      have to check all configurations since we don't know what type of
      connection this is. */
-  if (!(cconfig = silc_server_config_find_client_conn(server->config,
+  if (!(cconfig = silc_server_config_find_client(server->config,
                                                      sock->ip, port)))
-    cconfig = silc_server_config_find_client_conn(server->config,
+    cconfig = silc_server_config_find_client(server->config,
                                                  sock->hostname, 
                                                  port);
   if (!(sconfig = silc_server_config_find_server_conn(server->config,
@@ -1075,6 +1085,8 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
                                                  sock->hostname, 
                                                  port);
   if (!cconfig && !sconfig && !rconfig) {
+    SILC_LOG_INFO(("Connection %s (%s) is not allowed", 
+                   sock->hostname, sock->ip));
     silc_server_disconnect_remote(server, sock, 
                                  "Server closed connection: "
                                  "Connection refused");
@@ -1278,7 +1290,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   SilcSocketConnection sock = ctx->sock;
   SilcServerHBContext hb_context;
   SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
-  void *id_entry = NULL;
+  void *id_entry;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1316,7 +1328,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         and other information is created after we have received NEW_CLIENT
         packet from client. */
       client = silc_idlist_add_client(server->local_list, 
-                                     NULL, NULL, NULL, NULL, NULL, sock);
+                                     NULL, NULL, NULL, NULL, NULL, sock, 0);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
@@ -1340,7 +1352,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   case SILC_SOCKET_TYPE_ROUTER:
     {
       SilcServerEntry new_server;
-      SilcServerConfigSectionServerConnection *conn = 
+      /* XXX FIXME: Now server and router has different table, so this is probably broken. */
+      SilcServerConfigSectionRouter *conn =
        ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
        ctx->sconfig : ctx->rconfig;
 
@@ -1420,15 +1433,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       break;
     }
   default:
+    goto out;
     break;
   }
 
   sock->type = ctx->conn_type;
 
   /* Add the common data structure to the ID entry. */
-  if (id_entry)
-    silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
-      
+  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
+
   /* Add to sockets internal pointer for fast referencing */
   silc_free(sock->user_data);
   sock->user_data = id_entry;
@@ -1441,7 +1454,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
      timeout!! */
   hb_context = silc_calloc(1, sizeof(*hb_context));
   hb_context->server = server;
-  silc_socket_set_heartbeat(sock, 600, hb_context,
+  silc_socket_set_heartbeat(sock, 400, hb_context,
                            silc_server_perform_heartbeat,
                            server->schedule);
 
@@ -1541,7 +1554,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
        close the connection */
     if (SILC_IS_DISCONNECTING(sock)) {
       if (sock->user_data)
-       silc_server_free_sock_user_data(server, sock);
+       silc_server_free_sock_user_data(server, sock, NULL);
       silc_server_close_connection(server, sock);
       return;
     }
@@ -1549,8 +1562,20 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
     SILC_SET_DISCONNECTING(sock);
 
-    if (sock->user_data)
-      silc_server_free_sock_user_data(server, sock);
+    if (sock->user_data) {
+      char tmp[128];
+      if (silc_socket_get_error(sock, tmp, sizeof(tmp) - 1))
+       silc_server_free_sock_user_data(server, sock, tmp);
+      else
+       silc_server_free_sock_user_data(server, sock, NULL);
+    } else if (server->router_conn && server->router_conn->sock == sock &&
+            !server->router && server->standalone)
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_connect_to_router, 
+                            server, 1, 0,
+                            SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+
     silc_server_close_connection(server, sock);
     return;
   }
@@ -1574,9 +1599,23 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
  
   /* Process the packet. This will call the parser that will then
      decrypt and parse the packet. */
-  silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
-                             TRUE : FALSE, cipher, hmac, sequence, 
-                             silc_server_packet_parse, server);
+  ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
+                                   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 (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
+    silc_server_close_connection(server, sock);
+  }
 }
   
 /* Parses whole packet, received earlier. */
@@ -1628,7 +1667,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
        packet->dst_id_type == SILC_ID_SERVER && 
        sock->type != SILC_SOCKET_TYPE_CLIENT &&
-       memcmp(packet->dst_id, server->id_string, packet->dst_id_len)) {
+       memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
       
       /* Route the packet to fastest route for the destination ID */
       void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
@@ -2168,7 +2207,7 @@ void silc_server_packet_parse_type(SilcServer server,
 /* Creates connection to a remote router. */
 
 void silc_server_create_connection(SilcServer server,
-                                  char *remote_host, uint32 port)
+                                  const char *remote_host, uint32 port)
 {
   SilcServerConnection sconn;
 
@@ -2177,6 +2216,7 @@ void silc_server_create_connection(SilcServer server,
   sconn->server = server;
   sconn->remote_host = strdup(remote_host);
   sconn->remote_port = port;
+  sconn->no_reconnect = TRUE;
 
   silc_schedule_task_add(server->schedule, 0, 
                         silc_server_connect_router,
@@ -2285,7 +2325,7 @@ void silc_server_free_client_data(SilcServer server,
                                  SilcSocketConnection sock,
                                  SilcClientEntry client, 
                                  int notify,
-                                 char *signoff)
+                                 const char *signoff)
 {
   FreeClientInternal i = silc_calloc(1, sizeof(*i));
 
@@ -2305,11 +2345,19 @@ void silc_server_free_client_data(SilcServer server,
   /* Remove client from all channels */
   if (notify)
     silc_server_remove_from_channels(server, NULL, client, 
-                                    TRUE, signoff, TRUE);
+                                    TRUE, (char *)signoff, TRUE);
   else
     silc_server_remove_from_channels(server, NULL, client, 
                                     FALSE, NULL, FALSE);
     
+  /* Update statistics */
+  server->stat.my_clients--;
+  server->stat.clients--;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_clients--;
+  SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+  SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
   /* We will not delete the client entry right away. We will take it
      into history (for WHOWAS command) for 5 minutes */
   i->server = server;
@@ -2321,12 +2369,7 @@ void silc_server_free_client_data(SilcServer server,
   client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
   client->router = NULL;
   client->connection = NULL;
-
-  /* Free the client entry and everything in it */
-  server->stat.my_clients--;
-  server->stat.clients--;
-  if (server->server_type == SILC_ROUTER)
-    server->stat.cell_clients--;
+  client->mode = 0;
 }
 
 /* Frees user_data pointer from socket connection object. This also sends
@@ -2334,7 +2377,8 @@ void silc_server_free_client_data(SilcServer server,
    entities. */
 
 void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock)
+                                    SilcSocketConnection sock,
+                                    const char *signoff_message)
 {
   SILC_LOG_DEBUG(("Start"));
 
@@ -2342,7 +2386,8 @@ void silc_server_free_sock_user_data(SilcServer server,
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
+      silc_server_free_client_data(server, sock, user_data, TRUE, 
+                                  signoff_message);
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
@@ -2360,7 +2405,7 @@ void silc_server_free_sock_user_data(SilcServer server,
        /* Check whether we have a backup router connection */
        if (!backup_router || backup_router == user_data) {
          silc_schedule_task_add(server->schedule, 0, 
-                                silc_server_connect_to_router,
+                                silc_server_connect_to_router, 
                                 server, 1, 0,
                                 SILC_TASK_TIMEOUT,
                                 SILC_TASK_PRI_NORMAL);
@@ -2554,6 +2599,7 @@ void silc_server_remove_from_channels(SilcServer server,
          silc_hash_table_del(channel->user_list, chl2->client);
          silc_free(chl2);
        }
+       silc_hash_table_list_reset(&htl2);
        continue;
       }
 
@@ -2578,7 +2624,7 @@ void silc_server_remove_from_channels(SilcServer server,
     if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
       /* Re-generate channel key */
       if (!silc_server_create_channel_key(server, channel, 0))
-       return;
+       goto out;
       
       /* Send the channel key to the channel. The key of course is not sent
         to the client who was removed from the channel. */
@@ -2588,6 +2634,8 @@ void silc_server_remove_from_channels(SilcServer server,
     }
   }
 
+ out:
+  silc_hash_table_list_reset(&htl);
   silc_buffer_free(clidp);
 }
 
@@ -2674,6 +2722,7 @@ int silc_server_remove_from_one_channel(SilcServer server,
        silc_hash_table_del(channel->user_list, chl2->client);
        silc_free(chl2);
       }
+      silc_hash_table_list_reset(&htl2);
       return FALSE;
     }
 
@@ -2712,13 +2761,15 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
   /* If we have protocol active we must assure that we call the protocol's
      final callback so that all the memory is freed. */
   if (sock->protocol) {
+    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;
     return;
   }
 
   if (sock->user_data)
-    silc_server_free_sock_user_data(server, sock);
+    silc_server_free_sock_user_data(server, sock, NULL);
 
   silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                "Connection timeout");
@@ -2760,7 +2811,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 
   channel_name = strdup(channel_name);
 
-  /* Create the channel */
+  /* Create the channel ID */
   if (!silc_id_create_channel_id(server, router_id, server->rng, 
                                 &channel_id)) {
     silc_free(channel_name);
@@ -2768,13 +2819,16 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
     silc_hmac_free(newhmac);
     return NULL;
   }
+
+  /* Create the channel */
   entry = silc_idlist_add_channel(server->local_list, channel_name, 
                                  SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key, newhmac);
+                                 NULL, key, newhmac, 0);
   if (!entry) {
     silc_free(channel_name);
     silc_cipher_free(key);
     silc_hmac_free(newhmac);
+    silc_free(channel_id);
     return NULL;
   }
 
@@ -2784,11 +2838,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   /* Now create the actual key material */
   if (!silc_server_create_channel_key(server, entry, 
                                      silc_cipher_get_key_len(key) / 8)) {
-    silc_free(channel_name);
-    silc_cipher_free(key);
-    silc_hmac_free(newhmac);
-    silc_free(entry->cipher);
-    silc_free(entry->hmac_name);
+    silc_idlist_del_channel(server->local_list, entry);
     return NULL;
   }
 
@@ -2841,8 +2891,10 @@ silc_server_create_new_channel_with_id(SilcServer server,
   /* Create the channel */
   entry = silc_idlist_add_channel(server->local_list, channel_name, 
                                  SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key, newhmac);
+                                 NULL, key, newhmac, 0);
   if (!entry) {
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
     silc_free(channel_name);
     return NULL;
   }
@@ -2850,7 +2902,7 @@ silc_server_create_new_channel_with_id(SilcServer server,
   /* Now create the actual key material */
   if (!silc_server_create_channel_key(server, entry, 
                                      silc_cipher_get_key_len(key) / 8)) {
-    silc_free(channel_name);
+    silc_idlist_del_channel(server->local_list, entry);
     return NULL;
   }
 
@@ -2977,10 +3029,10 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   SILC_LOG_DEBUG(("Start"));
 
   /* Decode channel key payload */
-  payload = silc_channel_key_payload_parse(key_payload->data,
+  payload = silc_channel_key_payload_parse(key_payload->data, 
                                           key_payload->len);
   if (!payload) {
-    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    SILC_LOG_ERROR(("Bad channel key payload received, dropped"));
     channel = NULL;
     goto out;
   }
@@ -3000,7 +3052,8 @@ 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"));
+       SILC_LOG_ERROR(("Received key for non-existent channel %s",
+                       silc_id_render(id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -3085,8 +3138,7 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock,
 {
   SilcServerHBContext hb = (SilcServerHBContext)hb_context;
 
-  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
-                 sock->ip));
+  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
 
   /* Send the heartbeat */
   silc_server_send_heartbeat(hb->server, sock);
@@ -3142,6 +3194,19 @@ static void silc_server_announce_get_servers(SilcServer server,
   }
 }
 
+static SilcBuffer 
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer p;
+
+  va_start(ap, argc);
+  p = silc_notify_payload_encode(notify, argc, ap);
+  va_end(ap);
+  return p;
+}
+
 /* This function is used by router to announce existing servers to our
    primary router when we've connected to it. If `creation_time' is non-zero
    then only the servers that has been created after the `creation_time'
@@ -3185,12 +3250,15 @@ void silc_server_announce_servers(SilcServer server, bool global,
 static void silc_server_announce_get_clients(SilcServer server,
                                             SilcIDList id_list,
                                             SilcBuffer *clients,
+                                            SilcBuffer *umodes,
                                             unsigned long creation_time)
 {
   SilcIDCacheList list;
   SilcIDCacheEntry id_cache;
   SilcClientEntry client;
   SilcBuffer idp;
+  SilcBuffer tmp;
+  unsigned char mode[4];
 
   /* Go through all clients in the list */
   if (silc_idcache_get_all(id_list->clients, &list)) {
@@ -3213,6 +3281,20 @@ static void silc_server_announce_get_clients(SilcServer server,
        silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
        silc_buffer_put(*clients, idp->data, idp->len);
        silc_buffer_pull(*clients, idp->len);
+
+       SILC_PUT32_MSB(client->mode, mode);
+       tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+                                                2, idp->data, idp->len,
+                                                mode, 4);
+       *umodes = silc_buffer_realloc(*umodes, 
+                                     (*umodes ? 
+                                      (*umodes)->truelen + tmp->len :  
+                                      tmp->len));
+       silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+       silc_buffer_put(*umodes, tmp->data, tmp->len);
+       silc_buffer_pull(*umodes, tmp->len);
+       silc_buffer_free(tmp);
+
        silc_buffer_free(idp);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -3234,17 +3316,18 @@ void silc_server_announce_clients(SilcServer server,
                                  SilcSocketConnection remote)
 {
   SilcBuffer clients = NULL;
+  SilcBuffer umodes = NULL;
 
   SILC_LOG_DEBUG(("Announcing clients"));
 
   /* Get clients in local list */
   silc_server_announce_get_clients(server, server->local_list,
-                                  &clients, creation_time);
+                                  &clients, &umodes, creation_time);
 
   /* As router we announce our global list as well */
   if (server->server_type == SILC_ROUTER)
     silc_server_announce_get_clients(server, server->global_list,
-                                    &clients, creation_time);
+                                    &clients, &umodes, creation_time);
 
   if (clients) {
     silc_buffer_push(clients, clients->data - clients->head);
@@ -3257,19 +3340,36 @@ void silc_server_announce_clients(SilcServer server,
 
     silc_buffer_free(clients);
   }
+
+  if (umodes) {
+    silc_buffer_push(umodes, umodes->data - umodes->head);
+    SILC_LOG_HEXDUMP(("umodes"), umodes->data, umodes->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           umodes->data, umodes->len, TRUE);
+
+    silc_buffer_free(umodes);
+  }
 }
 
-static SilcBuffer 
-silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+/* Returns channel's topic for announcing it */
+
+void silc_server_announce_get_channel_topic(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *topic)
 {
-  va_list ap;
-  SilcBuffer p;
+  SilcBuffer chidp;
 
-  va_start(ap, argc);
-  p = silc_notify_payload_encode(notify, argc, ap);
-  va_end(ap);
-  return p;
+  if (channel->topic) {
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+    *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+                                               chidp->data, chidp->len,
+                                               channel->topic, 
+                                               strlen(channel->topic));
+    silc_buffer_free(chidp);
+  }
 }
 
 /* Returns assembled packets for channel users of the `channel'. */
@@ -3332,6 +3432,7 @@ void silc_server_announce_get_channel_users(SilcServer server,
 
     silc_buffer_free(clidp);
   }
+  silc_hash_table_list_reset(&htl);
   silc_buffer_free(chidp);
 }
 
@@ -3345,6 +3446,7 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
                                       uint32 *channel_users_modes_c,
+                                      SilcBuffer **channel_topics,
                                       SilcChannelID ***channel_ids,
                                       unsigned long creation_time)
 {
@@ -3394,6 +3496,7 @@ void silc_server_announce_get_channels(SilcServer server,
          silc_buffer_pull(*channels, len);
        }
 
+       /* Channel user modes */
        *channel_users_modes = silc_realloc(*channel_users_modes,
                                            sizeof(**channel_users_modes) * 
                                            (i + 1));
@@ -3405,6 +3508,13 @@ void silc_server_announce_get_channels(SilcServer server,
                                               channel_users,
                                               &(*channel_users_modes)[i]);
        (*channel_ids)[i] = channel->id;
+
+       /* Channel's topic */
+       *channel_topics = silc_realloc(*channel_topics,
+                                      sizeof(**channel_topics) * (i + 1));
+       (*channel_topics)[i] = NULL;
+       silc_server_announce_get_channel_topic(server, channel,
+                                              &(*channel_topics)[i]);
        i++;
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -3431,6 +3541,7 @@ void silc_server_announce_channels(SilcServer server,
 {
   SilcBuffer channels = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
+  SilcBuffer *channel_topics = NULL;
   uint32 channel_users_modes_c = 0;
   SilcChannelID **channel_ids = NULL;
 
@@ -3441,6 +3552,7 @@ void silc_server_announce_channels(SilcServer server,
                                    &channels, &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
+                                   &channel_topics,
                                    &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
@@ -3449,6 +3561,7 @@ void silc_server_announce_channels(SilcServer server,
                                      &channels, &channel_users,
                                      &channel_users_modes,
                                      &channel_users_modes_c,
+                                     &channel_topics,
                                      &channel_ids, creation_time);
 
   if (channels) {
@@ -3482,6 +3595,8 @@ void silc_server_announce_channels(SilcServer server,
     int i;
 
     for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_users_modes[i])
+        continue;
       silc_buffer_push(channel_users_modes[i], 
                       channel_users_modes[i]->data - 
                       channel_users_modes[i]->head);
@@ -3496,8 +3611,32 @@ void silc_server_announce_channels(SilcServer server,
       silc_buffer_free(channel_users_modes[i]);
     }
     silc_free(channel_users_modes);
-    silc_free(channel_ids);
   }
+
+  if (channel_topics) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_topics[i])
+       continue;
+
+      silc_buffer_push(channel_topics[i], 
+                      channel_topics[i]->data - 
+                      channel_topics[i]->head);
+      SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data, 
+                      channel_topics[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_topics[i]->data, 
+                                  channel_topics[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_topics[i]);
+    }
+    silc_free(channel_topics);
+  }
+
+  silc_free(channel_ids);
 }
 
 /* Failure timeout callback. If this is called then we will immediately
@@ -3537,6 +3676,7 @@ void silc_server_get_users_on_channel(SilcServer server,
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl))
     len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
+  silc_hash_table_list_reset(&htl);
 
   client_id_list = silc_buffer_alloc(len);
   client_mode_list = 
@@ -3558,6 +3698,7 @@ void silc_server_get_users_on_channel(SilcServer server,
 
     list_count++;
   }
+  silc_hash_table_list_reset(&htl);
   silc_buffer_push(client_id_list, 
                   client_id_list->data - client_id_list->head);
   silc_buffer_push(client_mode_list, 
@@ -3579,13 +3720,16 @@ void silc_server_save_users_on_channel(SilcServer server,
                                       uint32 user_count)
 {
   int i;
+  uint16 idp_len;
+  uint32 mode;
+  SilcClientID *client_id;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache;
+  bool global;
 
-  for (i = 0; i < user_count; i++) {
-    uint16 idp_len;
-    uint32 mode;
-    SilcClientID *client_id;
-    SilcClientEntry client;
+  SILC_LOG_DEBUG(("Start"));
 
+  for (i = 0; i < user_count; i++) {
     /* Client ID */
     SILC_GET16_MSB(idp_len, user_list->data + 2);
     idp_len += 4;
@@ -3602,14 +3746,18 @@ void silc_server_save_users_on_channel(SilcServer server,
       silc_free(client_id);
       continue;
     }
+
+    global = FALSE;
     
     /* Check if we have this client cached already. */
     client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                          server->server_type, NULL);
-    if (!client)
+                                          server->server_type, &cache);
+    if (!client) {
       client = silc_idlist_find_client_by_id(server->global_list, 
                                             client_id, server->server_type,
-                                            NULL);
+                                            &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. */
@@ -3623,7 +3771,7 @@ void silc_server_save_users_on_channel(SilcServer server,
         global. */
       client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
                                      silc_id_dup(client_id, SILC_ID_CLIENT), 
-                                     sock->user_data, NULL);
+                                     sock->user_data, NULL, 0);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
        silc_free(client_id);
@@ -3631,6 +3779,11 @@ 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;
     }
 
     silc_free(client_id);
@@ -3770,6 +3923,7 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
     silc_buffer_pull(buffer, len);
     silc_free(cid);
   }
+  silc_hash_table_list_reset(&htl);
 
   if (buffer)
     silc_buffer_push(buffer, buffer->data - buffer->head);
@@ -3781,10 +3935,14 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
    it using WHOIS command. */
 
 SilcClientEntry silc_server_get_client_resolve(SilcServer server,
-                                              SilcClientID *client_id)
+                                              SilcClientID *client_id,
+                                              bool *resolved)
 {
   SilcClientEntry client;
 
+  if (resolved)
+    *resolved = FALSE;
+
   client = silc_idlist_find_client_by_id(server->local_list, client_id,
                                         TRUE, NULL);
   if (!client) {
@@ -3814,6 +3972,10 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
                            buffer->data, buffer->len, FALSE);
     silc_buffer_free(idp);
     silc_buffer_free(buffer);
+
+    if (resolved)
+      *resolved = TRUE;
+
     return NULL;
   }