updates.
[silc.git] / apps / silcd / server.c
index 7d2c9584a93493152086caaa0b0ead7b209d871c..a41add7d9158fade694bdf2f45e16e1d058c7233 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 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
@@ -71,7 +71,9 @@ int silc_server_alloc(SilcServer *new_server)
 void silc_server_free(SilcServer server)
 {
   if (server) {
+#ifdef SILC_SIM
     SilcSimContext *sim;
+#endif
 
     if (server->local_list)
       silc_free(server->local_list);
@@ -80,11 +82,13 @@ void silc_server_free(SilcServer server)
     if (server->rng)
       silc_rng_free(server->rng);
 
+#ifdef SILC_SIM
     while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
       silc_dlist_del(server->sim, sim);
       silc_sim_free(sim);
     }
     silc_dlist_uninit(server->sim);
+#endif
 
     if (server->params)
       silc_free(server->params);
@@ -92,7 +96,6 @@ void silc_server_free(SilcServer server)
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
 
-    silc_math_primegen_uninit(); /* XXX */
     silc_free(server);
   }
 }
@@ -122,6 +125,7 @@ int silc_server_init(SilcServer server)
   server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
   server->params->retry_keep_trying = FALSE;
   server->params->protocol_timeout = 60;
+  server->params->require_reverse_mapping = FALSE;
 
   /* Set log files where log message should be saved. */
   server->config->server = server;
@@ -135,7 +139,7 @@ int silc_server_init(SilcServer server)
   /* Initialize random number generator for the server. */
   server->rng = silc_rng_alloc();
   silc_rng_init(server->rng);
-  silc_math_primegen_init(); /* XXX */
+  silc_rng_global_init(server->rng);
 
   /* Initialize hash functions for server to use */
   silc_hash_alloc("md5", &server->md5hash);
@@ -219,11 +223,9 @@ int silc_server_init(SilcServer server)
   server->local_list->servers = silc_idcache_alloc(0);
   server->local_list->channels = silc_idcache_alloc(0);
 
-  /* XXX for now these are allocated for normal server as well as these
-     hold some global information that the server has fetched from its
-     router. For router these are used as they are supposed to be used
-     on router. The XXX can be remoevd later if this is the way we are
-     going to do this in the normal server as well. */
+  /* These are allocated for normal server as well as these hold some 
+     global information that the server has fetched from its router. For 
+     router these are used as they are supposed to be used on router. */
   server->global_list->clients = silc_idcache_alloc(0);
   server->global_list->servers = silc_idcache_alloc(0);
   server->global_list->channels = silc_idcache_alloc(0);
@@ -346,6 +348,110 @@ int silc_server_init(SilcServer server)
   return FALSE;
 }
 
+/* Fork server to background and set gid+uid to non-root.
+   Silcd will not run as root, so trying to set either user or group to
+   root will cause silcd to exit. */
+
+void silc_server_daemonise(SilcServer server)
+{
+  /* Are we executing silcd as root or a regular user? */
+  if (geteuid()==0) {
+    
+    struct passwd *pw;
+    struct group *gr;
+    char *user, *group;
+    
+    if (!server->config->identity || !server->config->identity->user || 
+       !server->config->identity->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"
+       "\tuser account.  Modify the [Identity] configuration section to run\n"
+       "\tthe server as non-root user.\n");
+      exit(1);
+    }
+    
+    /* Get the values given for user and group in configuration file */
+    user=server->config->identity->user;
+    group=server->config->identity->group;
+    
+    /* Check whether the user/group information is text */ 
+    if (atoi(user)!=0 || atoi(group)!=0) {
+      SILC_LOG_DEBUG(("Invalid user and/or group information"));
+      SILC_LOG_DEBUG(("User and/or group given as number"));
+      fprintf(stderr, "Invalid user and/or group information\n");
+      fprintf(stderr, "Please assign them as names, not numbers\n");
+      exit(1);
+    }
+    
+    /* Catch the nasty incident of string "0" returning 0 from atoi */
+    if (strcmp("0", user)==0 || strcmp("0", group)==0) {
+      SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
+      fprintf(stderr, "User and/or group configured to 0. Exiting\n");
+      exit(1);
+    }
+    
+    pw=getpwnam(user);
+    gr=getgrnam(group);
+
+    if (!pw) {
+      fprintf(stderr, "No such user %s found\n", user);
+      exit(1);
+    }
+
+    if (!gr) {
+      fprintf(stderr, "No such group %s found\n", group);
+      exit(1);
+    }
+    
+    /* Check whether user and/or group is set to root. If yes, exit
+       immediately. Otherwise, setgid and setuid server to user.group */
+    if (gr->gr_gid==0 || pw->pw_uid==0) {
+      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"
+       "\tuser account.  Modify the [Identity] configuration section to run\n"
+       "\tthe server as non-root user.\n");
+      exit(1);
+    } else {
+      /* Fork server to background, making it a daemon */
+      if (fork()) {
+        SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+        SILC_LOG_DEBUG(("Forking SILC server to background"));
+        exit(0);
+      } 
+      setsid();
+      
+      SILC_LOG_DEBUG(("Changing to group %s", group));
+      if(setgid(gr->gr_gid)==0) {
+        SILC_LOG_DEBUG(("Setgid to %s", group));
+      } else {
+        SILC_LOG_DEBUG(("Setgid to %s failed", group));
+        fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
+                group);
+        exit(1);
+      }
+      SILC_LOG_DEBUG(("Changing to user nobody"));
+      if(setuid(pw->pw_uid)==0) {
+        SILC_LOG_DEBUG(("Setuid to %s", user));
+      } else {
+        SILC_LOG_DEBUG(("Setuid to %s failed", user));
+        fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
+                user);
+        exit(1);
+      }
+    }
+  } else {
+    /* Fork server to background, making it a daemon */
+    if (fork()) {
+      SILC_LOG_DEBUG(("Server started as user")); 
+      SILC_LOG_DEBUG(("Forking SILC server to background"));
+      exit(0);
+    }
+    setsid();
+  }
+}
+
 /* Stops the SILC server. This function is used to shutdown the server. 
    This is usually called after the scheduler has returned. After stopping 
    the server one should call silc_server_free. */
@@ -365,7 +471,9 @@ void silc_server_stop(SilcServer server)
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
-/* The heart of the server. This runs the scheduler thus runs the server. */
+/* The heart of the server. This runs the scheduler thus runs the server. 
+   When this returns the server has been stopped and the program will
+   be terminated. */
 
 void silc_server_run(SilcServer server)
 {
@@ -447,7 +555,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
      protocol. */
   silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
   server->sockets[sock] = newsocket;
-  newsocket->hostname = sconn->remote_host;
+  newsocket->hostname = strdup(sconn->remote_host);
+  newsocket->ip = strdup(sconn->remote_host);
   newsocket->port = sconn->remote_port;
   sconn->sock = newsocket;
 
@@ -591,7 +700,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    sock->protocol = NULL;
+    if (sock)
+      sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
@@ -674,6 +784,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcSocketConnection sock = ctx->sock;
   SilcServerEntry id_entry;
   SilcBuffer packet;
+  SilcServerHBContext hb_context;
   unsigned char *id_string;
 
   SILC_LOG_DEBUG(("Start"));
@@ -722,8 +833,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   /* Add the connected router to local server list */
   server->standalone = FALSE;
-  id_entry = silc_idlist_add_server(server->local_list, 
-                                   sock->hostname ? sock->hostname : sock->ip,
+  id_entry = silc_idlist_add_server(server->local_list, sock->hostname,
                                    SILC_ROUTER, ctx->dest_id, NULL, sock);
   if (!id_entry) {
     if (ctx->dest_id)
@@ -741,6 +851,23 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   server->router = id_entry;
   server->router->data.registered = TRUE;
 
+  /* Perform keepalive. The `hb_context' will be freed automatically
+     when finally calling the silc_socket_free function. XXX hardcoded 
+     timeout!! */
+  hb_context = silc_calloc(1, sizeof(*hb_context));
+  hb_context->server = server;
+  silc_socket_set_heartbeat(sock, 600, hb_context,
+                           silc_server_perform_heartbeat,
+                           server->timeout_queue);
+
+  /* If we are router then announce our possible servers. */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_announce_servers(server);
+
+  /* Announce our clients and channels to the router */
+  silc_server_announce_clients(server);
+  silc_server_announce_channels(server);
+
  out:
   /* Free the temporary connection data context */
   if (sconn)
@@ -768,9 +895,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   SILC_LOG_DEBUG(("Accepting new connection"));
 
+  server->stat.conn_attempts++;
+
   sock = silc_net_accept_connection(server->sock);
   if (sock < 0) {
     SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+    server->stat.conn_failures++;
     return;
   }
 
@@ -781,6 +911,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
       /*silc_server_send_notify("Server is full, trying to redirect..."); */
     } else {
       SILC_LOG_ERROR(("Refusing connection, server is full"));
+      server->stat.conn_failures++;
     }
     return;
   }
@@ -797,13 +928,17 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   /* XXX This MUST be done async as this will block the entire server
      process. Either we have to do our own resolver stuff or in the future
      we can use threads. */
-  /* Perform mandatory name and address lookups for the remote host. */
+  /* Perform name and address lookups for the remote host. */
   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if (!newsocket->ip || !newsocket->hostname) {
-    SILC_LOG_DEBUG(("IP lookup/DNS lookup failed"));
-    SILC_LOG_ERROR(("IP lookup/DNS lookup failed"));
+  if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+      !newsocket->ip) {
+    SILC_LOG_ERROR(("IP/DNS lookup failed"));
+    server->stat.conn_failures++;
     return;
   }
+  if (!newsocket->hostname)
+    newsocket->hostname = strdup(newsocket->ip);
+  newsocket->port = silc_net_get_remote_port(sock);
 
   SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
                 newsocket->ip));
@@ -820,6 +955,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      protocol but will not start it yet. The connector will be the
      initiator of the protocol thus we will wait for initiation from 
      there before we start the protocol. */
+  server->stat.auth_attempts++;
   silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
                      &newsocket->protocol, proto_ctx, 
                      silc_server_accept_new_connection_second);
@@ -875,6 +1011,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
       sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
+    server->stat.auth_failures++;
     return;
   }
 
@@ -925,6 +1062,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
+  SilcServerHBContext hb_context;
   void *id_entry = NULL;
 
   SILC_LOG_DEBUG(("Start"));
@@ -943,6 +1081,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       sock->protocol = NULL;
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
+    server->stat.auth_failures++;
     return;
   }
 
@@ -967,6 +1106,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
        break;
       }
 
+      /* Statistics */
+      server->stat.my_clients++;
+      server->stat.clients++;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_clients++;
+
       id_entry = (void *)client;
       break;
     }
@@ -998,6 +1143,13 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
        break;
       }
 
+      /* Statistics */
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.my_servers++;
+      else
+       server->stat.my_routers++;
+      server->stat.servers++;
+
       id_entry = (void *)new_server;
       
       /* There is connection to other server now, if it is router then
@@ -1029,6 +1181,15 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
   /* Connection has been fully established now. Everything is ok. */
   SILC_LOG_DEBUG(("New connection authenticated"));
 
+  /* Perform keepalive. The `hb_context' will be freed automatically
+     when finally calling the silc_socket_free function. XXX hardcoded 
+     timeout!! */
+  hb_context = silc_calloc(1, sizeof(*hb_context));
+  hb_context->server = server;
+  silc_socket_set_heartbeat(sock, 600, hb_context,
+                           silc_server_perform_heartbeat,
+                           server->timeout_queue);
+
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
@@ -1052,11 +1213,19 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   SilcHmac hmac = NULL;
   int ret;
 
+  if (!sock)
+    return;
+
   SILC_LOG_DEBUG(("Processing packet"));
 
   /* Packet sending */
+
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
+
+    server->stat.packets_sent++;
 
     if (sock->outbuf->data - sock->outbuf->head)
       silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
@@ -1067,6 +1236,9 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
        it later. */
     if (ret == -2)
       return;
+
+    if (ret == -1)
+      return;
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
@@ -1080,7 +1252,6 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   }
 
   /* Packet receiving */
-  SILC_LOG_DEBUG(("Reading data from connection"));
 
   /* Read some data from connection */
   ret = silc_packet_receive(sock);
@@ -1101,6 +1272,17 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     }
       
     SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+    SILC_SET_DISCONNECTING(sock);
+
+    /* If the closed connection was our primary router connection the
+       start re-connecting phase. */
+    if (!server->standalone && server->server_type == SILC_SERVER && 
+       sock == server->router->connection)
+      silc_task_register(server->timeout_queue, 0, 
+                        silc_server_connect_to_router,
+                        context, 0, 500000,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock);
@@ -1111,10 +1293,12 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   /* If connection is disconnecting or disconnected we will ignore
      what we read. */
   if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
-    SILC_LOG_DEBUG(("Ignoring read data from invalid connection"));
+    SILC_LOG_DEBUG(("Ignoring read data from disonnected connection"));
     return;
   }
 
+  server->stat.packets_received++;
+
   /* Get keys and stuff from ID entry */
   idata = (SilcIDListData)sock->user_data;
   if (idata) {
@@ -1162,12 +1346,15 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   if (server->server_type == SILC_ROUTER) {
     /* Route the packet if it is not destined to us. Other ID types but
        server are handled separately after processing them. */
-    if (packet->dst_id_type == SILC_ID_SERVER &&
+    if (packet->dst_id_type == SILC_ID_SERVER && 
        sock->type != SILC_SOCKET_TYPE_CLIENT &&
        SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
       
       /* Route the packet to fastest route for the destination ID */
-      void *id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
+      void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
+                               packet->dst_id_type);
+      if (!id)
+       goto out;
       silc_server_packet_route(server,
                               silc_server_route_get(server, id,
                                                     packet->dst_id_type),
@@ -1290,7 +1477,7 @@ void silc_server_packet_parse_type(SilcServer server,
   case SILC_PACKET_CHANNEL_MESSAGE:
     /*
      * Received channel message. Channel messages are special packets
-     * (although probably most common ones) hence they are handled
+     * (although probably most common ones) thus they are handled
      * specially.
      */
     SILC_LOG_DEBUG(("Channel Message packet"));
@@ -1313,7 +1500,8 @@ void silc_server_packet_parse_type(SilcServer server,
      */
   case SILC_PACKET_COMMAND:
     /*
-     * Recived command. Allocate command context and execute the command.
+     * Recived command. Processes the command request and allocates the
+     * command context and calls the command.
      */
     SILC_LOG_DEBUG(("Command packet"));
     silc_server_command_process(server, sock, packet);
@@ -1321,10 +1509,9 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_COMMAND_REPLY:
     /*
-     * Received command reply packet. Servers never send commands thus
-     * they don't receive command reply packets either, except in cases
-     * where server has forwarded command packet coming from client. 
-     * This must be the case here or we will ignore the packet.
+     * Received command reply packet. Received command reply to command. It
+     * may be reply to command sent by us or reply to command sent by client
+     * that we've routed further.
      */
     SILC_LOG_DEBUG(("Command Reply packet"));
     silc_server_command_reply(server, sock, packet);
@@ -1344,7 +1531,7 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
     /*
-     * XXX
+     * Private message key packet.
      */
     break;
 
@@ -1385,7 +1572,10 @@ void silc_server_packet_parse_type(SilcServer server,
 
       proto_ctx->packet = silc_packet_context_dup(packet);
       proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1410,7 +1600,10 @@ void silc_server_packet_parse_type(SilcServer server,
 
       proto_ctx->packet = silc_packet_context_dup(packet);
       proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
 
       /* Let the protocol handle the packet */
       sock->protocol->execute(server->timeout_queue, 0, 
@@ -1423,9 +1616,12 @@ void silc_server_packet_parse_type(SilcServer server,
     break;
 
   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
-    /* If we receive this packet we will send to the other end information
-       about our mandatory authentication method for the connection. 
-       This packet maybe received at any time. */
+    /*
+     * Connection authentication request packet. When we receive this packet
+     * we will send to the other end information about our mandatory
+     * authentication method for the connection. This packet maybe received
+     * at any time. 
+     */
     SILC_LOG_DEBUG(("Connection authentication request packet"));
     break;
 
@@ -1464,6 +1660,15 @@ void silc_server_packet_parse_type(SilcServer server,
     silc_server_new_id(server, sock, packet);
     break;
 
+  case SILC_PACKET_NEW_ID_LIST:
+    /*
+     * Received list of ID's. This packet is used by servers and routers
+     * to notify their primary router about clients and servers they have.
+     */
+    SILC_LOG_DEBUG(("New ID List packet"));
+    silc_server_new_id_list(server, sock, packet);
+    break;
+
   case SILC_PACKET_NEW_CLIENT:
     /*
      * Received new client packet. This includes client information that
@@ -1511,6 +1716,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * existing server or router connects to us and distributes information
      * of all channels it has.
      */
+    SILC_LOG_DEBUG(("New Channel List packet"));
+    silc_server_new_channel_list(server, sock, packet);
     break;
 
   case SILC_PACKET_NEW_CHANNEL_USER_LIST:
@@ -1519,6 +1726,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * when existing server or router connects to us and distributes 
      * information of all channel users it has.
      */
+    SILC_LOG_DEBUG(("New Channel User List packet"));
+    silc_server_new_channel_user_list(server, sock, packet);
     break;
 
   case SILC_PACKET_REPLACE_ID:
@@ -1536,6 +1745,7 @@ void silc_server_packet_parse_type(SilcServer server,
      * Received remove ID Packet. 
      */
     SILC_LOG_DEBUG(("Remove ID packet"));
+    silc_server_remove_id(server, sock, packet);
     break;
 
   case SILC_PACKET_REMOVE_CHANNEL_USER:
@@ -1547,6 +1757,21 @@ void silc_server_packet_parse_type(SilcServer server,
     silc_server_remove_channel_user(server, sock, packet);
     break;
 
+  case SILC_PACKET_SET_MODE:
+    /*
+     * Received packet to set the mode of channel or client's channel mode.
+     */
+    SILC_LOG_DEBUG(("Set Mode packet"));
+    silc_server_set_mode(server, sock, packet);
+    break;
+
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat.
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    break;
+
   default:
     SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1559,7 +1784,6 @@ void silc_server_packet_parse_type(SilcServer server,
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
-
   SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
 
   /* We won't listen for this connection anymore */
@@ -1585,6 +1809,9 @@ void silc_server_disconnect_remote(SilcServer server,
   va_list ap;
   unsigned char buf[4096];
 
+  if (!sock)
+    return;
+
   memset(buf, 0, sizeof(buf));
   va_start(ap, fmt);
   vsprintf(buf, fmt, ap);
@@ -1592,6 +1819,13 @@ void silc_server_disconnect_remote(SilcServer server,
 
   SILC_LOG_DEBUG(("Disconnecting remote host"));
 
+  SILC_LOG_INFO(("Disconnecting %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")));
+
   /* Notify remote end that the conversation is over. The notify message
      is tried to be sent immediately. */
   silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
@@ -1602,9 +1836,9 @@ void silc_server_disconnect_remote(SilcServer server,
   silc_server_close_connection(server, sock);
 }
 
-/* Free's user_data pointer from socket connection object. As this 
-   pointer maybe anything we wil switch here to find the correct
-   data type and free it the way it needs to be free'd. */
+/* Free's user_data pointer from socket connection object. This also sends
+   appropriate notify packets to the network to inform about leaving
+   entities. */
 
 void silc_server_free_sock_user_data(SilcServer server, 
                                     SilcSocketConnection sock)
@@ -1621,18 +1855,55 @@ void silc_server_free_sock_user_data(SilcServer server,
 
       /* XXX must take some info to history before freeing */
 
+      /* Send REMOVE_ID packet to routers. */
+      if (!server->standalone && server->router)
+       silc_server_send_remove_id(server, server->router->connection,
+                                  server->server_type == SILC_SERVER ?
+                                  FALSE : TRUE, user_data->id, 
+                                  SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
+
       /* Free the client entry and everything in it */
       silc_idlist_del_data(user_data);
       silc_idlist_del_client(server->local_list, user_data);
+      server->stat.my_clients--;
+      server->stat.clients--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_clients--;
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     {
+      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+
+      /* Send REMOVE_ID packet to routers. */
+      if (!server->standalone && server->router)
+       silc_server_send_remove_id(server, server->router->connection,
+                                  server->server_type == SILC_SERVER ?
+                                  FALSE : TRUE, user_data->id, 
+                                  SILC_ID_SERVER_LEN, SILC_ID_SERVER);
+
+      /* Then also free all client entries that this server owns as
+        they will become invalid now as well. */
+      silc_server_remove_clients_by_server(server, user_data);
+
+      /* If this was our primary router connection then we're lost to
+        the outside world. */
+      if (server->server_type == SILC_SERVER && server->router == user_data) {
+       server->id_entry->router = NULL;
+       server->router = NULL;
+       server->standalone = TRUE;
+      }
 
+      /* Free the server entry */
+      silc_idlist_del_data(user_data);
+      silc_idlist_del_server(server->local_list, user_data);
+      server->stat.my_servers--;
+      server->stat.servers--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_servers--;
       break;
     }
-    break;
   default:
     {
       SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
@@ -1646,9 +1917,105 @@ void silc_server_free_sock_user_data(SilcServer server,
   sock->user_data = NULL;
 }
 
-/* Removes client from all channels it has joined. This is used when
-   client connection is disconnected. If the client on a channel
-   is last, the channel is removed as well. */
+/* This function is used to remove all client entries by the server `entry'.
+   This is called when the connection is lost to the server. In this case
+   we must invalidate all the client entries owned by the server `entry'. */
+
+int silc_server_remove_clients_by_server(SilcServer server, 
+                                        SilcServerEntry entry)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+
+  if (silc_idcache_find_by_id(server->local_list->clients, 
+                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       
+       if (client->router != entry) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_from_channels(server, NULL, client);
+       silc_idlist_del_client(server->local_list, client);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+  
+  if (silc_idcache_find_by_id(server->global_list->clients, 
+                             SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       
+       if (client->router != entry) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       /* Remove the client entry */
+       silc_server_remove_from_channels(server, NULL, client);
+       silc_idlist_del_client(server->global_list, client);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+  
+  return TRUE;
+}
+
+/* Checks whether given channel has global users.  If it does this returns
+   TRUE and FALSE if there is only locally connected clients on the channel. */
+
+int silc_server_channel_has_global(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
+
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (chl->client->router)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Checks whether given channel has locally connected users.  If it does this
+   returns TRUE and FALSE if there is not one locally connected client. */
+
+int silc_server_channel_has_local(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
+
+  silc_list_start(channel->user_list);
+  while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+    if (!chl->client->router)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Removes client from all channels it has joined. This is used when client
+   connection is disconnected. If the client on a channel is last, the
+   channel is removed as well. This sends the SIGNOFF notify types. */
 
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
@@ -1656,7 +2023,7 @@ void silc_server_remove_from_channels(SilcServer server,
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
-  SilcBuffer chidp, clidp;
+  SilcBuffer clidp;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -1670,38 +2037,45 @@ void silc_server_remove_from_channels(SilcServer server,
   silc_list_start(client->channels);
   while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
     channel = chl->channel;
-    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
 
-    /* Remove from list */
+    /* Remove channel from client's channel list */
     silc_list_del(client->channels, chl);
 
-    /* If this client is last one on the channel the channel
-       is removed all together. */
-    if (silc_list_count(channel->user_list) < 2) {
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_list_count(channel->user_list) < 2) {
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
+      continue;
+    }
+
+    /* Remove client from channel's client list */
+    silc_list_del(channel->user_list, chl);
+    silc_free(chl);
+    server->stat.my_chanclients--;
 
-      /* However, if the channel has marked global users then the 
-        channel is not created locally, and this does not remove the
-        channel globally from SILC network, in this case we will
-        notify that this client has left the channel. */
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_local(channel)) {
+      /* Notify about leaving client if this channel has global users. */
       if (channel->global_users)
-       silc_server_send_notify_to_channel(server, channel, TRUE,
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                           SILC_NOTIFY_TYPE_SIGNOFF, 1,
                                           clidp->data, clidp->len);
-      
-      silc_idlist_del_channel(server->local_list, channel);
+
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      server->stat.my_channels--;
       continue;
     }
 
-    /* Remove from list */
-    silc_list_del(channel->user_list, chl);
-    silc_free(chl);
-
     /* Send notify to channel about client leaving SILC and thus
        the entire channel. */
-    silc_server_send_notify_to_channel(server, channel, TRUE,
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                       SILC_NOTIFY_TYPE_SIGNOFF, 1,
                                       clidp->data, clidp->len);
-    silc_buffer_free(chidp);
   }
 
   silc_buffer_free(clidp);
@@ -1736,31 +2110,50 @@ int silc_server_remove_from_one_channel(SilcServer server,
 
     ch = chl->channel;
 
-    /* Remove from list */
+    /* Remove channel from client's channel list */
     silc_list_del(client->channels, chl);
 
-    /* If this client is last one on the channel the channel
-       is removed all together. */
-    if (silc_list_count(channel->user_list) < 2) {
-      /* Notify about leaving client if this channel has global users,
-        ie. the channel is not created locally. */
-      if (notify && channel->global_users)
-       silc_server_send_notify_to_channel(server, channel, TRUE,
-                                          SILC_NOTIFY_TYPE_LEAVE, 1,
-                                          clidp->data, clidp->len);
-      
-      silc_idlist_del_channel(server->local_list, channel);
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_list_count(channel->user_list) < 2) {
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
       silc_buffer_free(clidp);
+      server->stat.my_channels--;
       return FALSE;
     }
 
-    /* Remove from list */
+    /* Remove client from channel's client list */
     silc_list_del(channel->user_list, chl);
     silc_free(chl);
+    server->stat.my_chanclients--;
+
+    /* If there is no global users on the channel anymore mark the channel
+       as local channel. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_global(channel))
+      channel->global_users = FALSE;
+
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type == SILC_SERVER &&
+       !silc_server_channel_has_local(channel)) {
+      /* Notify about leaving client if this channel has global users. */
+      if (notify && channel->global_users)
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                          SILC_NOTIFY_TYPE_LEAVE, 1,
+                                          clidp->data, clidp->len);
+
+      if (!silc_idlist_del_channel(server->local_list, channel))
+       silc_idlist_del_channel(server->global_list, channel);
+      silc_buffer_free(clidp);
+      server->stat.my_channels--;
+      return FALSE;
+    }
 
     /* Send notify to channel about client leaving the channel */
     if (notify)
-      silc_server_send_notify_to_channel(server, channel, TRUE,
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                         SILC_NOTIFY_TYPE_LEAVE, 1,
                                         clidp->data, clidp->len);
     break;
@@ -1797,10 +2190,16 @@ int silc_server_client_on_channel(SilcClientEntry client,
 
 SILC_TASK_CALLBACK(silc_server_timeout_remote)
 {
-  SilcServerConnection sconn = (SilcServerConnection)context;
-  SilcSocketConnection sock = sconn->server->sockets[fd];
+  SilcServer server = (SilcServer)context;
+  SilcSocketConnection sock = server->sockets[fd];
+
+  if (!sock)
+    return;
+
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock);
 
-  silc_server_disconnect_remote(sconn->server, sock, 
+  silc_server_disconnect_remote(server, sock, 
                                "Server closed connection: "
                                "Connection timeout");
 }
@@ -1813,7 +2212,8 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
 SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
                                                SilcServerID *router_id,
                                                char *cipher, 
-                                               char *channel_name)
+                                               char *channel_name,
+                                               int broadcast)
 {
   SilcChannelID *channel_id;
   SilcChannelEntry entry;
@@ -1844,11 +2244,59 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (server->standalone == FALSE) {
+  if (broadcast && server->standalone == FALSE) {
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
                                 channel_name, entry->id, SILC_ID_CHANNEL_LEN);
   }
 
+  server->stat.my_channels++;
+
+  return entry;
+}
+
+/* Same as above but creates the channel with Channel ID `channel_id. */
+
+SilcChannelEntry 
+silc_server_create_new_channel_with_id(SilcServer server, 
+                                      char *cipher, 
+                                      char *channel_name,
+                                      SilcChannelID *channel_id,
+                                      int broadcast)
+{
+  SilcChannelEntry entry;
+  SilcCipher key;
+
+  SILC_LOG_DEBUG(("Creating new channel"));
+
+  if (!cipher)
+    cipher = "twofish";
+
+  /* Allocate cipher */
+  silc_cipher_alloc(cipher, &key);
+
+  channel_name = strdup(channel_name);
+
+  /* Create the channel */
+  entry = silc_idlist_add_channel(server->local_list, channel_name, 
+                                 SILC_CHANNEL_MODE_NONE, channel_id, 
+                                 NULL, key);
+  if (!entry) {
+    silc_free(channel_name);
+    return NULL;
+  }
+
+  /* Now create the actual key material */
+  silc_server_create_channel_key(server, entry, 16);
+
+  /* Notify other routers about the new channel. We send the packet
+     to our primary route. */
+  if (broadcast && server->standalone == FALSE) {
+    silc_server_send_new_channel(server, server->router->connection, TRUE, 
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
+  }
+
+  server->stat.my_channels++;
+
   return entry;
 }
 
@@ -1864,12 +2312,15 @@ void silc_server_create_channel_key(SilcServer server,
   unsigned char channel_key[32];
   unsigned int len;
 
+  if (!channel->channel_key)
+    silc_cipher_alloc("twofish", &channel->channel_key);
+
   if (key_len)
     len = key_len;
   else if (channel->key_len)
     len = channel->key_len / 8;
   else
-    len = 32;
+    len = sizeof(channel_key);
 
   /* Create channel key */
   for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
@@ -1880,7 +2331,7 @@ void silc_server_create_channel_key(SilcServer server,
 
   /* Remove old key if exists */
   if (channel->key) {
-    memset(channel->key, 0, channel->key_len);
+    memset(channel->key, 0, channel->key_len / 8);
     silc_free(channel->key);
   }
 
@@ -1890,3 +2341,364 @@ void silc_server_create_channel_key(SilcServer server,
   memcpy(channel->key, channel_key, len);
   memset(channel_key, 0, sizeof(channel_key));
 }
+
+/* Saves the channel key found in the encoded `key_payload' buffer. This 
+   function is used when we receive Channel Key Payload and also when we're
+   processing JOIN command reply. Returns entry to the channel. */
+
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel)
+{
+  SilcChannelKeyPayload payload = NULL;
+  SilcChannelID *id = NULL;
+  unsigned char *tmp;
+  unsigned int tmp_len;
+  char *cipher;
+
+  /* Decode channel key payload */
+  payload = silc_channel_key_payload_parse(key_payload);
+  if (!payload) {
+    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+    channel = NULL;
+    goto out;
+  }
+
+  /* Get the channel entry */
+  if (!channel) {
+
+    /* Get channel ID */
+    tmp = silc_channel_key_get_id(payload, &tmp_len);
+    id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
+    if (!id) {
+      channel = NULL;
+      goto out;
+    }
+
+    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+    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"));
+       goto out;
+      }
+    }
+  }
+
+  tmp = silc_channel_key_get_key(payload, &tmp_len);
+  if (!tmp) {
+    channel = NULL;
+    goto out;
+  }
+
+  cipher = silc_channel_key_get_cipher(payload, NULL);;
+  if (!cipher) {
+    channel = NULL;
+    goto out;
+  }
+
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+    silc_cipher_free(channel->channel_key);
+  }
+
+  /* Create new cipher */
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel = NULL;
+    goto out;
+  }
+
+  /* Save the key */
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
+  memcpy(channel->key, tmp, tmp_len);
+  channel->channel_key->cipher->set_key(channel->channel_key->context, 
+                                       tmp, tmp_len);
+
+ out:
+  if (id)
+    silc_free(id);
+  if (payload)
+    silc_channel_key_payload_free(payload);
+
+  return channel;
+}
+
+/* Heartbeat callback. This function is set as argument for the
+   silc_socket_set_heartbeat function. The library will call this function
+   at the set time interval. */
+
+void silc_server_perform_heartbeat(SilcSocketConnection sock,
+                                  void *hb_context)
+{
+  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
+
+  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
+                 sock->ip));
+
+  /* Send the heartbeat */
+  silc_server_send_heartbeat(hb->server, sock);
+}
+
+/* Returns assembled of all servers in the given ID list. The packet's
+   form is dictated by the New ID payload. */
+
+static void silc_server_announce_get_servers(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *servers)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+  SilcBuffer idp;
+
+  /* Go through all clients in the list */
+  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
+                             SILC_ID_SERVER, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       entry = (SilcServerEntry)id_cache->context;
+
+       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+
+       *servers = silc_buffer_realloc(*servers, 
+                                      (*servers ? 
+                                       (*servers)->truelen + idp->len : 
+                                       idp->len));
+       silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
+       silc_buffer_put(*servers, idp->data, idp->len);
+       silc_buffer_pull(*servers, idp->len);
+       silc_buffer_free(idp);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+
+    silc_idcache_list_free(list);
+  }
+}
+
+/* This function is used by router to announce existing servers to our
+   primary router when we've connected to it. */
+
+void silc_server_announce_servers(SilcServer server)
+{
+  SilcBuffer servers = NULL;
+
+  SILC_LOG_DEBUG(("Announcing servers"));
+
+  /* Get servers in local list */
+  silc_server_announce_get_servers(server, server->local_list, &servers);
+
+  /* Get servers in global list */
+  silc_server_announce_get_servers(server, server->global_list, &servers);
+
+  if (servers) {
+    silc_buffer_push(servers, servers->data - servers->head);
+    SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_ID_LIST, 0,
+                           servers->data, servers->len, TRUE);
+
+    silc_buffer_free(servers);
+  }
+}
+
+/* Returns assembled packet of all clients in the given ID list. The
+   packet's form is dictated by the New ID Payload. */
+
+static void silc_server_announce_get_clients(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *clients)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcClientEntry client;
+  SilcBuffer idp;
+
+  /* Go through all clients in the list */
+  if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
+                             SILC_ID_CLIENT, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+
+       idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+       *clients = silc_buffer_realloc(*clients, 
+                                      (*clients ? 
+                                       (*clients)->truelen + idp->len : 
+                                       idp->len));
+       silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
+       silc_buffer_put(*clients, idp->data, idp->len);
+       silc_buffer_pull(*clients, idp->len);
+       silc_buffer_free(idp);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+
+    silc_idcache_list_free(list);
+  }
+}
+
+/* This function is used to announce our existing clients to our router
+   when we've connected to it. */
+
+void silc_server_announce_clients(SilcServer server)
+{
+  SilcBuffer clients = NULL;
+
+  SILC_LOG_DEBUG(("Announcing clients"));
+
+  /* Get clients in local list */
+  silc_server_announce_get_clients(server, server->local_list,
+                                  &clients);
+
+  /* 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);
+
+  if (clients) {
+    silc_buffer_push(clients, clients->data - clients->head);
+    SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_ID_LIST, 0,
+                           clients->data, clients->len, TRUE);
+
+    silc_buffer_free(clients);
+  }
+}
+
+/* Returns assembled packets for all channels and users on those channels
+   from the given ID List. The packets are in the form dictated by the
+   New Channel and New Channel User payloads. */
+
+static void silc_server_announce_get_channels(SilcServer server,
+                                             SilcIDList id_list,
+                                             SilcBuffer *channels,
+                                             SilcBuffer *channel_users)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  unsigned char *cid;
+  unsigned short name_len;
+  int len;
+
+  /* Go through all channels in the list */
+  if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY, 
+                             SILC_ID_CHANNEL, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       channel = (SilcChannelEntry)id_cache->context;
+       
+       cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       name_len = strlen(channel->channel_name);
+
+       len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+       *channels = 
+         silc_buffer_realloc(*channels, 
+                             (*channels ? (*channels)->truelen + len : len));
+       silc_buffer_pull_tail(*channels, 
+                             ((*channels)->end - (*channels)->data));
+       silc_buffer_format(*channels,
+                          SILC_STR_UI_SHORT(name_len),
+                          SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                               name_len),
+                          SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                          SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                          SILC_STR_END);
+       silc_buffer_pull(*channels, len);
+
+       /* Now find all users on the channel */
+       silc_list_start(channel->user_list);
+       while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         unsigned char *clid;
+
+         clid = silc_id_id2str(chl->client->id, SILC_ID_CLIENT);
+         
+         len = 4 + SILC_ID_CHANNEL_LEN + SILC_ID_CLIENT_LEN;
+         *channel_users = 
+           silc_buffer_realloc(*channel_users, 
+                               (*channel_users ? 
+                                (*channel_users)->truelen + len : len));
+         silc_buffer_pull_tail(*channel_users, 
+                               ((*channel_users)->end - 
+                                (*channel_users)->data));
+         silc_buffer_format(*channel_users,
+                            SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                            SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                            SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+                            SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
+                            SILC_STR_END);
+         silc_buffer_pull(*channel_users, len);
+         silc_free(clid);
+       }
+
+       silc_free(cid);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+
+    silc_idcache_list_free(list);
+  }
+}
+
+/* This function is used to announce our existing channels to our router
+   when we've connected to it. This also announces the users on the
+   channels to the router. */
+
+void silc_server_announce_channels(SilcServer server)
+{
+  SilcBuffer channels = NULL, channel_users = NULL;
+
+  SILC_LOG_DEBUG(("Announcing channels and channel users"));
+
+  /* Get channels and channel users in local list */
+  silc_server_announce_get_channels(server, server->local_list,
+                                   &channels, &channel_users);
+
+  /* Get channels and channel users in global list */
+  silc_server_announce_get_channels(server, server->global_list,
+                                   &channels, &channel_users);
+
+  if (channels) {
+    silc_buffer_push(channels, channels->data - channels->head);
+    SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_CHANNEL_LIST, 0,
+                           channels->data, channels->len,
+                           FALSE);
+
+    silc_buffer_free(channels);
+  }
+
+  if (channel_users) {
+    silc_buffer_push(channel_users, channel_users->data - channel_users->head);
+    SILC_LOG_HEXDUMP(("channel users"), channel_users->data, 
+                    channel_users->len);
+
+    /* Send the packet */
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_NEW_CHANNEL_USER_LIST, 0,
+                           channel_users->data, channel_users->len,
+                           FALSE);
+
+    silc_buffer_free(channel_users);
+  }
+}