updates.
[silc.git] / apps / silcd / server.c
index 0d351bc00df39d9e430f6735042e9ee61870f23e..765f6be552630cffa935a80a39e1c75e33439625 100644 (file)
@@ -38,6 +38,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
 SILC_TASK_CALLBACK(silc_server_packet_process);
 SILC_TASK_CALLBACK(silc_server_packet_parse_real);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
+SILC_TASK_CALLBACK(silc_server_failure_callback);
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -96,7 +97,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);
   }
 }
@@ -130,17 +130,18 @@ int silc_server_init(SilcServer server)
 
   /* Set log files where log message should be saved. */
   server->config->server = server;
-  silc_config_server_setlogfiles(server->config);
+  silc_server_config_setlogfiles(server->config);
  
   /* Register all configured ciphers, PKCS and hash functions. */
-  silc_config_server_register_ciphers(server->config);
-  silc_config_server_register_pkcs(server->config);
-  silc_config_server_register_hashfuncs(server->config);
+  silc_server_config_register_ciphers(server->config);
+  silc_server_config_register_pkcs(server->config);
+  silc_server_config_register_hashfuncs(server->config);
+  silc_server_config_register_hmacs(server->config);
 
   /* 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);
@@ -274,8 +275,6 @@ int silc_server_init(SilcServer server)
        is sent as argument for fast referencing in the future. */
     silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, 
                      &newsocket);
-    if (!newsocket)
-      goto err0;
 
     server->sockets[sock[i]] = newsocket;
 
@@ -623,7 +622,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
-      sconn->remote_host = server->config->routers->host;
+      sconn->remote_host = strdup(server->config->routers->host);
       sconn->remote_port = server->config->routers->port;
 
       silc_task_register(server->timeout_queue, fd, 
@@ -637,7 +636,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   /* If we are a SILC router we need to establish all of our primary
      routes. */
   if (server->server_type == SILC_ROUTER) {
-    SilcConfigServerSectionServerConnection *ptr;
+    SilcServerConfigSectionServerConnection *ptr;
 
     SILC_LOG_DEBUG(("We are router"));
 
@@ -653,7 +652,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
        /* Allocate connection object for hold connection specific stuff. */
        sconn = silc_calloc(1, sizeof(*sconn));
        sconn->server = server;
-       sconn->remote_host = ptr->host;
+       sconn->remote_host = strdup(ptr->host);
        sconn->remote_port = ptr->port;
 
        silc_task_register(server->timeout_queue, fd, 
@@ -694,6 +693,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -701,12 +701,39 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    sock->protocol = NULL;
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
   }
   
+  /* We now have the key material as the result of the key exchange
+     protocol. Take the key material into use. Free the raw key material
+     as soon as we've set them into use. */
+  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+                                       ctx->ske->prop->cipher,
+                                       ctx->ske->prop->pkcs,
+                                       ctx->ske->prop->hash,
+                                       ctx->ske->prop->hmac,
+                                       ctx->responder)) {
+    silc_protocol_free(protocol);
+    silc_ske_free_key_material(ctx->keymat);
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    if (ctx->dest_id)
+      silc_free(ctx->dest_id);
+    silc_free(ctx);
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_failure_callback);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Key exchange failed");
+    return;
+  }    
+  silc_ske_free_key_material(ctx->keymat);
+
   /* Allocate internal context for the authentication protocol. This
      is sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
@@ -718,12 +745,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   proto_ctx->dest_id = ctx->dest_id;
 
   /* Resolve the authentication method used in this connection */
-  proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+  proto_ctx->auth_meth = SILC_AUTH_PASSWORD;
   if (server->config->routers) {
-    SilcConfigServerSectionServerConnection *conn = NULL;
+    SilcServerConfigSectionServerConnection *conn = NULL;
 
     /* Check if we find a match from user configured connections */
-    conn = silc_config_server_find_router_conn(server->config,
+    conn = silc_server_config_find_router_conn(server->config,
                                               sock->hostname,
                                               sock->port);
     if (conn) {
@@ -870,8 +897,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
  out:
   /* Free the temporary connection data context */
-  if (sconn)
+  if (sconn) {
+    silc_free(sconn->remote_host);
     silc_free(sconn);
+  }
 
   /* Free the protocol object */
   silc_protocol_free(protocol);
@@ -1000,6 +1029,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
@@ -1007,14 +1037,41 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     silc_free(ctx);
-    if (sock)
-      sock->protocol = NULL;
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     server->stat.auth_failures++;
     return;
   }
 
+  /* We now have the key material as the result of the key exchange
+     protocol. Take the key material into use. Free the raw key material
+     as soon as we've set them into use. */
+  if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+                                       ctx->ske->prop->cipher,
+                                       ctx->ske->prop->pkcs,
+                                       ctx->ske->prop->hash,
+                                       ctx->ske->prop->hmac,
+                                       ctx->responder)) {
+    silc_protocol_free(protocol);
+    silc_ske_free_key_material(ctx->keymat);
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    if (ctx->dest_id)
+      silc_free(ctx->dest_id);
+    silc_free(ctx);
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_failure_callback);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Key exchange failed");
+    server->stat.auth_failures++;
+    return;
+  }    
+  silc_ske_free_key_material(ctx->keymat);
+
   /* Allocate internal context for the authentication protocol. This
      is sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
@@ -1079,6 +1136,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     silc_free(ctx);
     if (sock)
       sock->protocol = NULL;
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
     server->stat.auth_failures++;
@@ -1190,6 +1249,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
                            silc_server_perform_heartbeat,
                            server->timeout_queue);
 
+  silc_task_unregister_by_callback(server->timeout_queue,
+                                  silc_server_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
@@ -1276,11 +1337,11 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
     /* If the closed connection was our primary router connection the
        start re-connecting phase. */
-    if (!server->standalone && server->server_type == SILC_SERVER && 
+    if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && 
        sock == server->router->connection)
       silc_task_register(server->timeout_queue, 0, 
                         silc_server_connect_to_router,
-                        context, 0, 500000,
+                        context, 1, 0,
                         SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
 
@@ -1427,6 +1488,8 @@ void silc_server_packet_parse_type(SilcServer server,
   switch(type) {
   case SILC_PACKET_DISCONNECT:
     SILC_LOG_DEBUG(("Disconnect packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     break;
 
   case SILC_PACKET_SUCCESS:
@@ -1436,6 +1499,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * success message is for whatever protocol is executing currently.
      */
     SILC_LOG_DEBUG(("Success packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     if (sock->protocol) {
       sock->protocol->execute(server->timeout_queue, 0,
                              sock->protocol, sock->sock, 0, 0);
@@ -1449,16 +1514,25 @@ void silc_server_packet_parse_type(SilcServer server,
      * failure message is for whatever protocol is executing currently.
      */
     SILC_LOG_DEBUG(("Failure packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     if (sock->protocol) {
-      /* XXX Audit the failure type */
-      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-      sock->protocol->execute(server->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
+      SilcServerFailureContext f;
+      f = silc_calloc(1, sizeof(*f));
+      f->server = server;
+      f->sock = sock;
+      
+      /* We will wait 5 seconds to process this failure packet */
+      silc_task_register(server->timeout_queue, sock->sock,
+                        silc_server_failure_callback, (void *)f, 5, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     }
     break;
 
   case SILC_PACKET_REJECT:
     SILC_LOG_DEBUG(("Reject packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     return;
     break;
 
@@ -1468,7 +1542,10 @@ void silc_server_packet_parse_type(SilcServer server,
      * router. Server then relays the notify messages to clients if needed.
      */
     SILC_LOG_DEBUG(("Notify packet"));
-    silc_server_notify(server, sock, packet);
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_notify_list(server, sock, packet);
+    else
+      silc_server_notify(server, sock, packet);
     break;
 
     /* 
@@ -1481,6 +1558,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * specially.
      */
     SILC_LOG_DEBUG(("Channel Message packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_channel_message(server, sock, packet);
     break;
 
@@ -1492,6 +1571,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * never receives this channel and thus is ignored.
      */
     SILC_LOG_DEBUG(("Channel Key packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_channel_key(server, sock, packet);
     break;
 
@@ -1504,6 +1585,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * command context and calls the command.
      */
     SILC_LOG_DEBUG(("Command packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_command_process(server, sock, packet);
     break;
 
@@ -1514,6 +1597,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * that we've routed further.
      */
     SILC_LOG_DEBUG(("Command Reply packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_command_reply(server, sock, packet);
     break;
 
@@ -1526,6 +1611,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * client or server.
      */
     SILC_LOG_DEBUG(("Private Message packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_private_message(server, sock, packet);
     break;
 
@@ -1533,6 +1620,9 @@ void silc_server_packet_parse_type(SilcServer server,
     /*
      * Private message key packet.
      */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_private_message_key(server, sock, packet);
     break;
 
     /*
@@ -1540,6 +1630,9 @@ void silc_server_packet_parse_type(SilcServer server,
      */
   case SILC_PACKET_KEY_EXCHANGE:
     SILC_LOG_DEBUG(("KE packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
     if (sock->protocol && sock->protocol->protocol->type 
        == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
@@ -1561,6 +1654,9 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_KEY_EXCHANGE_1:
     SILC_LOG_DEBUG(("KE 1 packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
     if (sock->protocol && sock->protocol->protocol->type 
        == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
@@ -1589,6 +1685,9 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_KEY_EXCHANGE_2:
     SILC_LOG_DEBUG(("KE 2 packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
     if (sock->protocol && sock->protocol->protocol->type 
        == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
@@ -1623,6 +1722,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * at any time. 
      */
     SILC_LOG_DEBUG(("Connection authentication request packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     break;
 
     /*
@@ -1632,6 +1733,9 @@ void silc_server_packet_parse_type(SilcServer server,
     /* Start of the authentication protocol. We receive here the 
        authentication data and will verify it. */
     SILC_LOG_DEBUG(("Connection auth packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
     if (sock->protocol && sock->protocol->protocol->type 
        == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
 
@@ -1657,16 +1761,10 @@ void silc_server_packet_parse_type(SilcServer server,
      * SILC network.
      */
     SILC_LOG_DEBUG(("New ID packet"));
-    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);
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_new_id_list(server, sock, packet);
+    else
+      silc_server_new_id(server, sock, packet);
     break;
 
   case SILC_PACKET_NEW_CLIENT:
@@ -1676,6 +1774,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * ID we will send it to the client.
      */
     SILC_LOG_DEBUG(("New Client packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_new_client(server, sock, packet);
     break;
 
@@ -1686,6 +1786,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * connected to us.
      */
     SILC_LOG_DEBUG(("New Server packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_new_server(server, sock, packet);
     break;
 
@@ -1695,81 +1797,29 @@ void silc_server_packet_parse_type(SilcServer server,
      * network are distributed using this packet.
      */
     SILC_LOG_DEBUG(("New Channel packet"));
-    silc_server_new_channel(server, sock, packet);
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_new_channel_list(server, sock, packet);
+    else
+      silc_server_new_channel(server, sock, packet);
     break;
 
-  case SILC_PACKET_NEW_CHANNEL_USER:
-    /*
-     * Received new channel user packet. Information about new users on a
-     * channel are distributed between routers using this packet.  The
-     * router receiving this will redistribute it and also sent JOIN notify
-     * to local clients on the same channel. Normal server sends JOIN notify
-     * to its local clients on the channel.
-     */
-    SILC_LOG_DEBUG(("New Channel User packet"));
-    silc_server_new_channel_user(server, sock, packet);
-    break;
-
-  case SILC_PACKET_NEW_CHANNEL_LIST:
-    /*
-     * List of new channel packets received. This is usually received when
-     * 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:
-    /*
-     * List of new channel user packets received. This is usually received
-     * 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:
-    /*
-     * Received replace ID packet. This sends the old ID that is to be
-     * replaced with the new one included into the packet. Client must not
-     * send this packet.
-     */
-    SILC_LOG_DEBUG(("Replace ID packet"));
-    silc_server_replace_id(server, sock, packet);
-    break;
-
-  case SILC_PACKET_REMOVE_ID:
-    /*
-     * Received remove ID Packet. 
-     */
-    SILC_LOG_DEBUG(("Remove ID packet"));
-    silc_server_remove_id(server, sock, packet);
-    break;
-
-  case SILC_PACKET_REMOVE_CHANNEL_USER:
-    /*
-     * Received packet to remove user from a channel. Routers notify other
-     * routers about a user leaving a channel.
-     */
-    SILC_LOG_DEBUG(("Remove Channel User packet"));
-    silc_server_remove_channel_user(server, sock, packet);
-    break;
-
-  case SILC_PACKET_SET_MODE:
+  case SILC_PACKET_HEARTBEAT:
     /*
-     * Received packet to set the mode of channel or client's channel mode.
+     * Received heartbeat.
      */
-    SILC_LOG_DEBUG(("Set Mode packet"));
-    silc_server_set_mode(server, sock, packet);
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     break;
 
-  case SILC_PACKET_HEARTBEAT:
+  case SILC_PACKET_KEY_AGREEMENT:
     /*
      * Received heartbeat.
      */
-    SILC_LOG_DEBUG(("Heartbeat packet"));
+    SILC_LOG_DEBUG(("Key agreement packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_key_agreement(server, sock, packet);
     break;
 
   default:
@@ -1779,6 +1829,25 @@ void silc_server_packet_parse_type(SilcServer server,
   
 }
 
+/* Creates connection to a remote router. */
+
+void silc_server_create_connection(SilcServer server,
+                                  char *remote_host, unsigned int port)
+{
+  SilcServerConnection sconn;
+
+  /* Allocate connection object for hold connection specific stuff. */
+  sconn = silc_calloc(1, sizeof(*sconn));
+  sconn->server = server;
+  sconn->remote_host = strdup(remote_host);
+  sconn->remote_port = port;
+
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_connect_router,
+                    (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                    SILC_TASK_PRI_NORMAL);
+}
+
 /* Closes connection to socket connection */
 
 void silc_server_close_connection(SilcServer server,
@@ -1819,7 +1888,7 @@ void silc_server_disconnect_remote(SilcServer server,
 
   SILC_LOG_DEBUG(("Disconnecting remote host"));
 
-  SILC_LOG_INFO(("Disconnecting %s:%d (%s) [%s]", sock->hostname,
+  SILC_LOG_INFO(("Disconnecting %s:%d [%s]", sock->hostname,
                   sock->port,
                   (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                    sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
@@ -1836,7 +1905,34 @@ void silc_server_disconnect_remote(SilcServer server,
   silc_server_close_connection(server, sock);
 }
 
-/* Free's user_data pointer from socket connection object. This also sends
+/* Frees client data and notifies about client's signoff. */
+
+void silc_server_free_client_data(SilcServer server, 
+                                 SilcSocketConnection sock,
+                                 SilcClientEntry user_data, char *signoff)
+{
+  /* Send REMOVE_ID packet to routers. */
+  if (!server->standalone && server->router)
+    silc_server_send_notify_signoff(server, server->router->connection,
+                                   server->server_type == SILC_SERVER ?
+                                   FALSE : TRUE, user_data->id, 
+                                   SILC_ID_CLIENT_LEN, signoff);
+
+  /* Remove client from all channels */
+  silc_server_remove_from_channels(server, sock, user_data, signoff);
+
+  /* XXX must take some info to history before freeing */
+
+  /* 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--;
+}
+
+/* Frees user_data pointer from socket connection object. This also sends
    appropriate notify packets to the network to inform about leaving
    entities. */
 
@@ -1849,26 +1945,7 @@ void silc_server_free_sock_user_data(SilcServer server,
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-
-      /* Remove client from all channels */
-      silc_server_remove_from_channels(server, sock, user_data);
-
-      /* 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--;
+      silc_server_free_client_data(server, sock, user_data, NULL);
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
@@ -1878,10 +1955,12 @@ void silc_server_free_sock_user_data(SilcServer server,
 
       /* 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);
+       silc_server_send_notify_server_signoff(server, 
+                                              server->router->connection,
+                                              server->server_type == 
+                                              SILC_SERVER ?
+                                              FALSE : TRUE, user_data->id, 
+                                              SILC_ID_SERVER_LEN);
 
       /* Then also free all client entries that this server owns as
         they will become invalid now as well. */
@@ -1928,6 +2007,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
   SilcIDCacheEntry id_cache = NULL;
   SilcClientEntry client = NULL;
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (silc_idcache_find_by_id(server->local_list->clients, 
                              SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
 
@@ -1943,7 +2024,7 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client);
+       silc_server_remove_from_channels(server, NULL, client, NULL);
        silc_idlist_del_client(server->local_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -1968,7 +2049,7 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client);
+       silc_server_remove_from_channels(server, NULL, client, NULL);
        silc_idlist_del_client(server->global_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2019,7 +2100,8 @@ int silc_server_channel_has_local(SilcChannelEntry channel)
 
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
-                                     SilcClientEntry client)
+                                     SilcClientEntry client,
+                                     char *signoff_message)
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
@@ -2055,6 +2137,12 @@ void silc_server_remove_from_channels(SilcServer server,
     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 &&
@@ -2062,8 +2150,11 @@ void silc_server_remove_from_channels(SilcServer server,
       /* Notify about leaving client if this channel has global users. */
       if (channel->global_users)
        silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                          SILC_NOTIFY_TYPE_SIGNOFF, 1,
-                                          clidp->data, clidp->len);
+                                          SILC_NOTIFY_TYPE_SIGNOFF, 
+                                          signoff_message ? 2 : 1,
+                                          clidp->data, clidp->len,
+                                          signoff_message, signoff_message ?
+                                          strlen(signoff_message) : 0);
 
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
@@ -2074,8 +2165,11 @@ void silc_server_remove_from_channels(SilcServer server,
     /* Send notify to channel about client leaving SILC and thus
        the entire channel. */
     silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_SIGNOFF, 1,
-                                      clidp->data, clidp->len);
+                                      SILC_NOTIFY_TYPE_SIGNOFF, 
+                                      signoff_message ? 2 : 1,
+                                      clidp->data, clidp->len,
+                                      signoff_message, signoff_message ?
+                                      strlen(signoff_message) : 0);
   }
 
   silc_buffer_free(clidp);
@@ -2212,6 +2306,7 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
 SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
                                                SilcServerID *router_id,
                                                char *cipher, 
+                                               char *hmac,
                                                char *channel_name,
                                                int broadcast)
 {
@@ -2222,10 +2317,13 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   SILC_LOG_DEBUG(("Creating new channel"));
 
   if (!cipher)
-    cipher = "twofish";
+    cipher = "aes-256-cbc";
+  if (!hmac)
+    hmac = "hmac-sha1-96";
 
   /* Allocate cipher */
-  silc_cipher_alloc(cipher, &key);
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
 
   channel_name = strdup(channel_name);
 
@@ -2233,14 +2331,15 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
   silc_id_create_channel_id(router_id, server->rng, &channel_id);
   entry = silc_idlist_add_channel(server->local_list, channel_name, 
                                  SILC_CHANNEL_MODE_NONE, channel_id, 
-                                 NULL, key);
+                                 NULL, key, hmac);
   if (!entry) {
     silc_free(channel_name);
     return NULL;
   }
 
   /* Now create the actual key material */
-  silc_server_create_channel_key(server, entry, 16);
+  silc_server_create_channel_key(server, entry, 
+                                silc_cipher_get_key_len(key) / 8);
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
@@ -2259,6 +2358,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 SilcChannelEntry 
 silc_server_create_new_channel_with_id(SilcServer server, 
                                       char *cipher, 
+                                      char *hmac,
                                       char *channel_name,
                                       SilcChannelID *channel_id,
                                       int broadcast)
@@ -2269,24 +2369,28 @@ silc_server_create_new_channel_with_id(SilcServer server,
   SILC_LOG_DEBUG(("Creating new channel"));
 
   if (!cipher)
-    cipher = "twofish";
+    cipher = "aes-256-cbc";
+  if (!hmac)
+    hmac = "hmac-sha1-96";
 
   /* Allocate cipher */
-  silc_cipher_alloc(cipher, &key);
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
 
   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);
+                                 NULL, key, hmac);
   if (!entry) {
     silc_free(channel_name);
     return NULL;
   }
 
   /* Now create the actual key material */
-  silc_server_create_channel_key(server, entry, 16);
+  silc_server_create_channel_key(server, entry, 
+                                silc_cipher_get_key_len(key) / 8);
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
@@ -2313,14 +2417,15 @@ void silc_server_create_channel_key(SilcServer server,
   unsigned int len;
 
   if (!channel->channel_key)
-    silc_cipher_alloc("twofish", &channel->channel_key);
+    if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
+      return;
 
   if (key_len)
     len = key_len;
   else if (channel->key_len)
     len = channel->key_len / 8;
   else
-    len = sizeof(channel_key);
+    len = silc_cipher_get_key_len(channel->channel_key) / 8;
 
   /* Create channel key */
   for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
@@ -2391,7 +2496,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     goto out;
   }
 
-  cipher = silc_channel_key_get_cipher(payload, NULL);;
+  cipher = silc_channel_key_get_cipher(payload, NULL);
   if (!cipher) {
     channel = NULL;
     goto out;
@@ -2502,7 +2607,7 @@ void silc_server_announce_servers(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_ID_LIST, 0,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
                            servers->data, servers->len, TRUE);
 
     silc_buffer_free(servers);
@@ -2572,13 +2677,22 @@ void silc_server_announce_clients(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_ID_LIST, 0,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
                            clients->data, clients->len, TRUE);
 
     silc_buffer_free(clients);
   }
 }
 
+static SilcBuffer 
+silc_server_announce_encode_join(unsigned int argc, ...)
+{
+  va_list ap;
+
+  va_start(ap, argc);
+  return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
+}
+
 /* 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. */
@@ -2592,6 +2706,7 @@ static void silc_server_announce_get_channels(SilcServer server,
   SilcIDCacheEntry id_cache;
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
+  SilcBuffer chidp;
   unsigned char *cid;
   unsigned short name_len;
   int len;
@@ -2622,13 +2737,17 @@ static void silc_server_announce_get_channels(SilcServer server,
        silc_buffer_pull(*channels, len);
 
        /* Now find all users on the channel */
+       chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
        silc_list_start(channel->user_list);
        while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
-         unsigned char *clid;
+         SilcBuffer clidp;
+         SilcBuffer tmp;
+
+         clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
 
-         clid = silc_id_id2str(chl->client->id, SILC_ID_CLIENT);
-         
-         len = 4 + SILC_ID_CHANNEL_LEN + SILC_ID_CLIENT_LEN;
+         tmp = silc_server_announce_encode_join(2, clidp->data, clidp->len,
+                                                chidp->data, chidp->len);
+         len = tmp->len;
          *channel_users = 
            silc_buffer_realloc(*channel_users, 
                                (*channel_users ? 
@@ -2636,15 +2755,13 @@ static void silc_server_announce_get_channels(SilcServer server,
          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_put(*channel_users, tmp->data, tmp->len);
          silc_buffer_pull(*channel_users, len);
-         silc_free(clid);
+         silc_buffer_free(clidp);
+         silc_buffer_free(tmp);
        }
+       silc_buffer_free(chidp);
 
        silc_free(cid);
 
@@ -2681,7 +2798,7 @@ void silc_server_announce_channels(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_CHANNEL_LIST, 0,
+                           SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
                            channels->data, channels->len,
                            FALSE);
 
@@ -2695,10 +2812,30 @@ void silc_server_announce_channels(SilcServer server)
 
     /* Send the packet */
     silc_server_packet_send(server, server->router->connection,
-                           SILC_PACKET_NEW_CHANNEL_USER_LIST, 0,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
                            channel_users->data, channel_users->len,
                            FALSE);
 
     silc_buffer_free(channel_users);
   }
 }
+
+/* Failure timeout callback. If this is called then we will immediately
+   process the received failure. We always process the failure with timeout
+   since we do not want to blindly trust to received failure packets. 
+   This won't be called (the timeout is cancelled) if the failure was
+   bogus (it is bogus if remote does not close the connection after sending
+   the failure). */
+
+SILC_TASK_CALLBACK(silc_server_failure_callback)
+{
+  SilcServerFailureContext f = (SilcServerFailureContext)context;
+
+  if (f->sock->protocol) {
+    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+    f->sock->protocol->execute(f->server->timeout_queue, 0,
+                              f->sock->protocol, f->sock->sock, 0, 0);
+  }
+
+  silc_free(f);
+}