updates.
[silc.git] / lib / silcclient / client.c
index 8f40f8599085e273d2e021f2d2a9603e49cf06c8..36c4c93d50613744a23bb06b9dcaa3b658ba2bf4 100644 (file)
@@ -27,6 +27,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
 SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
 SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
 SILC_TASK_CALLBACK(silc_client_packet_parse_real);
+SILC_TASK_CALLBACK(silc_client_rekey_callback);
+SILC_TASK_CALLBACK(silc_client_rekey_final);
 
 static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
 static void silc_client_packet_parse_type(SilcClient client, 
@@ -140,9 +142,9 @@ SilcClientConnection silc_client_add_connection(SilcClient client,
   conn = silc_calloc(1, sizeof(*conn));
 
   /* Initialize ID caches */
-  conn->client_cache = silc_idcache_alloc(0, NULL);
-  conn->channel_cache = silc_idcache_alloc(0, NULL);
-  conn->server_cache = silc_idcache_alloc(0, NULL);
+  conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT, NULL);
+  conn->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+  conn->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
   conn->client = client;
   conn->remote_host = strdup(hostname);
   conn->remote_port = port;
@@ -189,21 +191,21 @@ void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
 
   if (!client->sockets) {
     client->sockets = silc_calloc(1, sizeof(*client->sockets));
-    client->sockets[0] = sock;
+    client->sockets[0] = silc_socket_dup(sock);
     client->sockets_count = 1;
     return;
   }
 
   for (i = 0; i < client->sockets_count; i++) {
     if (client->sockets[i] == NULL) {
-      client->sockets[i] = sock;
+      client->sockets[i] = silc_socket_dup(sock);
       return;
     }
   }
 
   client->sockets = silc_realloc(client->sockets, sizeof(*client->sockets) *
                                 (client->sockets_count + 1));
-  client->sockets[client->sockets_count] = sock;
+  client->sockets[client->sockets_count] = silc_socket_dup(sock);
   client->sockets_count++;
 }
 
@@ -218,6 +220,7 @@ void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
 
   for (i = 0; i < client->sockets_count; i++) {
     if (client->sockets[i] == sock) {
+      silc_socket_free(sock);
       client->sockets[i] = NULL;
       return;
     }
@@ -317,7 +320,7 @@ int silc_client_start_key_exchange(SilcClient client,
      protocol as context. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->client = (void *)client;
-  proto_ctx->sock = conn->sock;
+  proto_ctx->sock = silc_socket_dup(conn->sock);
   proto_ctx->rng = client->rng;
   proto_ctx->responder = FALSE;
   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
@@ -345,7 +348,7 @@ int silc_client_start_key_exchange(SilcClient client,
   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
 
   /* Execute the protocol */
-  protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
+  silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
   return TRUE;
 }
 
@@ -432,8 +435,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     ctx->sock->protocol = NULL;
-    silc_task_unregister_by_callback(client->timeout_queue,
-                                    silc_client_failure_callback);
+    silc_socket_free(ctx->sock);
 
     /* Notify application of failure */
     client->ops->connect(client, ctx->sock->user_data, FALSE);
@@ -448,7 +450,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
                                   ctx->ske->prop->cipher,
                                   ctx->ske->prop->pkcs,
                                   ctx->ske->prop->hash,
-                                  ctx->ske->prop->hmac);
+                                  ctx->ske->prop->hmac,
+                                  ctx->ske->prop->group);
   silc_ske_free_key_material(ctx->keymat);
 
   /* Allocate internal context for the authentication protocol. This
@@ -464,12 +467,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   if (!client->ops->get_auth_method(client, sock->user_data, sock->hostname,
                                    sock->port, &proto_ctx->auth_meth,
                                    &proto_ctx->auth_data, 
-                                   &proto_ctx->auth_data_len)) {
-    client->ops->say(client, ctx->sock->user_data, 
-                    "Could not resolve authentication method to use, "
-                    "assume no authentication");
+                                   &proto_ctx->auth_data_len))
     proto_ctx->auth_meth = SILC_AUTH_NONE;
-  }
 
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
@@ -478,16 +477,13 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
   silc_free(ctx);
   sock->protocol = NULL;
 
-  /* Allocate the authentication protocol. This is allocated here
-     but we won't start it yet. We will be receiving party of this
-     protocol thus we will wait that connecting party will make
-     their first move. */
+  /* Allocate the authenteication protocol and execute it. */
   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, 
                      &sock->protocol, (void *)proto_ctx, 
                      silc_client_connect_to_server_final);
 
   /* Execute the protocol */
-  sock->protocol->execute(client->timeout_queue, 0, sock->protocol, fd, 0, 0);
+  silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
 }
 
 /* Finalizes the connection to the remote SILC server. This is called
@@ -518,8 +514,7 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
     conn->sock->protocol = NULL;
-    silc_task_unregister_by_callback(client->timeout_queue,
-                                    silc_client_failure_callback);
+    silc_socket_free(ctx->sock);
 
     /* Notify application of failure */
     client->ops->connect(client, ctx->sock->user_data, FALSE);
@@ -551,15 +546,22 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   /* Save remote ID. */
   conn->remote_id = ctx->dest_id;
   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
-  conn->remote_id_data_len = SILC_ID_SERVER_LEN;
+  conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
+
+  /* Register re-key timeout */
+  conn->rekey->timeout = 3600; /* XXX hardcoded */
+  conn->rekey->context = (void *)client;
+  silc_task_register(client->timeout_queue, conn->sock->sock, 
+                    silc_client_rekey_callback,
+                    (void *)conn->sock, conn->rekey->timeout, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 
-  silc_task_unregister_by_callback(client->timeout_queue,
-                                  silc_client_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->auth_data)
     silc_free(ctx->auth_data);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  silc_socket_free(ctx->sock);
   silc_free(ctx);
   conn->sock->protocol = NULL;
 }
@@ -570,10 +572,16 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 
 int silc_client_packet_send_real(SilcClient client,
                                 SilcSocketConnection sock,
-                                int force_send)
+                                bool force_send,
+                                bool flush)
 {
   int ret;
 
+  /* If rekey protocol is active we must assure that all packets are
+     sent through packet queue. */
+  if (flush == FALSE && SILC_CLIENT_IS_REKEY(sock))
+    force_send = FALSE;
+
   /* Send the packet */
   ret = silc_packet_send(sock, force_send);
   if (ret != -2)
@@ -619,7 +627,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
       silc_buffer_push(sock->outbuf, 
                       sock->outbuf->data - sock->outbuf->head);
 
-    ret = silc_client_packet_send_real(client, sock, TRUE);
+    ret = silc_client_packet_send_real(client, sock, TRUE, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
@@ -669,7 +677,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
     /* Process the packet. This will call the parser that will then
        decrypt and parse the packet. */
     if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
-      silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
+      silc_packet_receive_process(sock, conn->receive_key, conn->hmac_receive,
                                  silc_client_packet_parse, client);
     else
       silc_packet_receive_process(sock, NULL, NULL,
@@ -721,7 +729,8 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
 
   /* Decrypt the received packet */
   if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
-    ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
+    ret = silc_packet_decrypt(conn->receive_key, conn->hmac_receive, 
+                             buffer, packet,
                              silc_client_packet_decrypt_check, parse_ctx);
   else
     ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
@@ -765,7 +774,7 @@ void silc_client_packet_parse(SilcPacketParserContext *parser_context)
                     SILC_TASK_TIMEOUT,
                     SILC_TASK_PRI_NORMAL);
 }
-  
+
 /* Parses the packet type and calls what ever routines the packet type
    requires. This is done for all incoming packets. */
 
@@ -789,10 +798,8 @@ void silc_client_packet_parse_type(SilcClient client,
      * one protocol for connection executing at once hence this
      * success message is for whatever protocol is executing currently.
      */
-    if (sock->protocol) {
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    }
+    if (sock->protocol)
+      silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
     break;
   case SILC_PACKET_FAILURE:
     /*
@@ -867,35 +874,46 @@ void silc_client_packet_parse_type(SilcClient client,
        break;
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
+      silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
                      "protocol active, packet dropped."));
-
-      /* XXX Trigger KE protocol?? Rekey actually! */
     }
     break;
 
   case SILC_PACKET_KEY_EXCHANGE_1:
     if (sock->protocol && sock->protocol->protocol && 
-       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
-      SilcClientKEInternalContext *proto_ctx = 
-       (SilcClientKEInternalContext *)sock->protocol->context;
-
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
-
-      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_len,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
-
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
+       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
+
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+       SilcClientRekeyInternalContext *proto_ctx = 
+         (SilcClientRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+      } else {
+       SilcClientKEInternalContext *proto_ctx = 
+         (SilcClientKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       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_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
+       
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -903,23 +921,37 @@ void silc_client_packet_parse_type(SilcClient client,
     break;
   case SILC_PACKET_KEY_EXCHANGE_2:
     if (sock->protocol && sock->protocol->protocol && 
-       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
-      SilcClientKEInternalContext *proto_ctx = 
-       (SilcClientKEInternalContext *)sock->protocol->context;
-
-      if (proto_ctx->packet)
-       silc_packet_context_free(proto_ctx->packet);
-
-      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_len,
-                                         packet->src_id_type);
-      if (!proto_ctx->dest_id)
-       break;
-
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
+       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
+
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+       SilcClientRekeyInternalContext *proto_ctx = 
+         (SilcClientRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+      } else {
+       SilcClientKEInternalContext *proto_ctx = 
+         (SilcClientKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       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_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
+       
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -962,6 +994,38 @@ void silc_client_packet_parse_type(SilcClient client,
     silc_client_key_agreement(client, sock, packet);
     break;
 
+  case SILC_PACKET_REKEY:
+    SILC_LOG_DEBUG(("Re-key packet"));
+    /* We ignore this for now */
+    break;
+
+  case SILC_PACKET_REKEY_DONE:
+    SILC_LOG_DEBUG(("Re-key done packet"));
+
+    if (sock->protocol && sock->protocol->protocol && 
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+
+      SilcClientRekeyInternalContext *proto_ctx = 
+       (SilcClientRekeyInternalContext *)sock->protocol->context;
+      
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
+      
+      proto_ctx->packet = silc_packet_context_dup(packet);
+
+      /* Let the protocol handle the packet */
+      if (proto_ctx->responder == FALSE)
+       silc_protocol_execute(sock->protocol, client->timeout_queue, 0, 0);
+      else
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->timeout_queue, 
+                             0, 100000);
+    } else {
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
+    }
+    break;
+
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -987,6 +1051,9 @@ void silc_client_packet_send(SilcClient client,
 {
   SilcPacketContext packetdata;
 
+  if (!sock)
+    return;
+
   SILC_LOG_DEBUG(("Sending packet, type %d", type));
 
   /* Get data used in the packet sending, keys and stuff */
@@ -994,8 +1061,8 @@ void silc_client_packet_send(SilcClient client,
     if (!cipher && ((SilcClientConnection)sock->user_data)->send_key)
       cipher = ((SilcClientConnection)sock->user_data)->send_key;
 
-    if (!hmac && ((SilcClientConnection)sock->user_data)->hmac)
-      hmac = ((SilcClientConnection)sock->user_data)->hmac;
+    if (!hmac && ((SilcClientConnection)sock->user_data)->hmac_send)
+      hmac = ((SilcClientConnection)sock->user_data)->hmac_send;
 
     if (!dst_id && ((SilcClientConnection)sock->user_data)->remote_id) {
       dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
@@ -1007,15 +1074,19 @@ void silc_client_packet_send(SilcClient client,
   packetdata.flags = 0;
   packetdata.type = type;
   if (sock->user_data && 
-      ((SilcClientConnection)sock->user_data)->local_id_data)
+      ((SilcClientConnection)sock->user_data)->local_id_data) {
     packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
-  else 
+    packetdata.src_id_len = 
+      silc_id_get_len(((SilcClientConnection)sock->user_data)->local_id,
+                     SILC_ID_CLIENT);
+  } else { 
     packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+    packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+  }
   packetdata.src_id_type = SILC_ID_CLIENT;
   if (dst_id) {
     packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type);
-    packetdata.dst_id_len = silc_id_get_len(dst_id_type);
+    packetdata.dst_id_len = silc_id_get_len(dst_id, dst_id_type);
     packetdata.dst_id_type = dst_id_type;
   } else {
     packetdata.dst_id = NULL;
@@ -1053,7 +1124,7 @@ void silc_client_packet_send(SilcClient client,
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
+  silc_client_packet_send_real(client, sock, force_send, FALSE);
 }
 
 /* Closes connection to remote end. Free's all allocated data except
@@ -1085,16 +1156,34 @@ void silc_client_close_connection(SilcClient client,
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
 
+  /* Cancel any active protocol */
+  if (sock->protocol) {
+    if (sock->protocol->protocol->type == 
+       SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == 
+       SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute_final(sock->protocol, client->timeout_queue);
+      sock->protocol = NULL;
+      /* The application will recall this function with these protocols
+        (the ops->connect client operation). */
+      return;
+    } else {
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute_final(sock->protocol, client->timeout_queue);
+      sock->protocol = NULL;
+    }
+  }
+
   /* Free everything */
   if (del && sock->user_data) {
     /* XXX Free all client entries and channel entries. */
 
-    client->ops->say(client, sock->user_data,
-                    "Closed connection to host %s", sock->hostname);
-
     /* Clear ID caches */
-    silc_idcache_del_all(conn->client_cache);
-    silc_idcache_del_all(conn->channel_cache);
+    if (conn->client_cache)
+      silc_idcache_del_all(conn->client_cache);
+    if (conn->channel_cache)
+      silc_idcache_del_all(conn->channel_cache);
 
     /* Free data */
     if (conn->remote_host)
@@ -1107,36 +1196,17 @@ void silc_client_close_connection(SilcClient client,
       silc_cipher_free(conn->send_key);
     if (conn->receive_key)
       silc_cipher_free(conn->receive_key);
-    if (conn->hmac)
-      silc_hmac_free(conn->hmac);
-    if (conn->hmac_key) {
-      memset(conn->hmac_key, 0, conn->hmac_key_len);
-      silc_free(conn->hmac_key);
-    }
+    if (conn->hmac_send)       /* conn->hmac_receive is same */
+      silc_hmac_free(conn->hmac_send);
     if (conn->pending_commands)
       silc_dlist_uninit(conn->pending_commands);
+    if (conn->rekey)
+      silc_free(conn->rekey);
 
-    conn->sock = NULL;
-    conn->remote_port = 0;
-    conn->remote_type = 0;
-    conn->send_key = NULL;
-    conn->receive_key = NULL;
-    conn->hmac = NULL;
-    conn->hmac_key = NULL;
-    conn->hmac_key_len = 0;
-    conn->local_id = NULL;
-    conn->local_id_data = NULL;
-    conn->remote_host = NULL;
-    conn->current_channel = NULL;
-    conn->pending_commands = NULL;
-
+    memset(conn, 0, sizeof(*conn));
     silc_client_del_connection(client, conn);
   }
 
-  if (sock->protocol) {
-    silc_protocol_free(sock->protocol);
-    sock->protocol = NULL;
-  }
   silc_socket_free(sock);
 }
 
@@ -1190,11 +1260,13 @@ void silc_client_receive_new_id(SilcClient client,
     connecting = TRUE;
 
   /* Delete old ID from ID cache */
-  silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, conn->local_id);
+  if (conn->local_id) {
+    silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
+    silc_free(conn->local_id);
+  }
   
   /* Save the new ID */
-  if (conn->local_id)
-    silc_free(conn->local_id);
+
   if (conn->local_id_data)
     silc_free(conn->local_id_data);
 
@@ -1217,9 +1289,8 @@ void silc_client_receive_new_id(SilcClient client,
   conn->local_entry->id = conn->local_id;
   
   /* Put it to the ID cache */
-  silc_idcache_add(conn->client_cache, conn->nickname, strlen(conn->nickname),
-                  SILC_ID_CLIENT, conn->local_id, (void *)conn->local_entry,
-                  TRUE, FALSE);
+  silc_idcache_add(conn->client_cache, conn->nickname, conn->local_id, 
+                  (void *)conn->local_entry, FALSE);
 
   /* Notify application of successful connection. We do it here now that
      we've received the Client ID and are allowed to send traffic. */
@@ -1251,9 +1322,8 @@ SilcChannelEntry silc_client_new_channel_id(SilcClient client,
   conn->current_channel = channel;
 
   /* Put it to the ID cache */
-  silc_idcache_add(conn->channel_cache, channel_name, strlen(channel_name),
-                  SILC_ID_CHANNEL, (void *)channel->id, (void *)channel, 
-                  TRUE, FALSE);
+  silc_idcache_add(conn->channel_cache, channel_name, (void *)channel->id, 
+                  (void *)channel, FALSE);
 
   return channel;
 }
@@ -1271,8 +1341,7 @@ void silc_client_remove_from_channels(SilcClient client,
   SilcChannelEntry channel;
   SilcChannelUser chu;
 
-  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
-                              SILC_ID_CHANNEL, &list))
+  if (!silc_idcache_get_all(conn->channel_cache, &list))
     return;
 
   silc_idcache_list_first(list, &id_cache);
@@ -1314,8 +1383,7 @@ void silc_client_replace_from_channels(SilcClient client,
   SilcChannelEntry channel;
   SilcChannelUser chu;
 
-  if (!silc_idcache_find_by_id(conn->channel_cache, SILC_ID_CACHE_ANY,
-                              SILC_ID_CHANNEL, &list))
+  if (!silc_idcache_get_all(conn->channel_cache, &list))
     return;
 
   silc_idcache_list_first(list, &id_cache);
@@ -1341,148 +1409,97 @@ void silc_client_replace_from_channels(SilcClient client,
   silc_idcache_list_free(list);
 }
 
-/* Parses mode mask and returns the mode as string. */
+/* Registers failure timeout to process the received failure packet
+   with timeout. */
 
-char *silc_client_chmode(uint32 mode, SilcChannelEntry channel)
+void silc_client_process_failure(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
 {
-  char string[100];
-
-  if (!mode)
-    return NULL;
-
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_MODE_PRIVATE)
-    strncat(string, "p", 1);
-
-  if (mode & SILC_CHANNEL_MODE_SECRET)
-    strncat(string, "s", 1);
-
-  if (mode & SILC_CHANNEL_MODE_PRIVKEY)
-    strncat(string, "k", 1);
-
-  if (mode & SILC_CHANNEL_MODE_INVITE)
-    strncat(string, "i", 1);
-
-  if (mode & SILC_CHANNEL_MODE_TOPIC)
-    strncat(string, "t", 1);
-
-  if (mode & SILC_CHANNEL_MODE_ULIMIT)
-    strncat(string, "l", 1);
-
-  if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
-    strncat(string, "a", 1);
-
-  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
-    strncat(string, "f", 1);
+  uint32 failure = 0;
 
-  if (mode & SILC_CHANNEL_MODE_CIPHER) {
-    char cipher[30];
-    memset(cipher, 0, sizeof(cipher));
-    snprintf(cipher, sizeof(cipher), " c (%s)", 
-            channel->channel_key->cipher->name);
-    strncat(string, cipher, strlen(cipher));
-  }
+  if (sock->protocol) {
+    if (packet->buffer->len >= 4)
+      SILC_GET32_MSB(failure, packet->buffer->data);
 
-  if (mode & SILC_CHANNEL_MODE_HMAC) {
-    char hmac[30];
-    memset(hmac, 0, sizeof(hmac));
-    snprintf(hmac, sizeof(hmac), " h (%s)", 
-            channel->hmac->hmac->name);
-    strncat(string, hmac, strlen(hmac));
+    /* Notify application */
+    client->ops->failure(client, sock->user_data, sock->protocol,
+                        (void *)failure);
   }
-
-  /* Rest of mode is ignored */
-
-  return strdup(string);
 }
 
-/* Parses channel user mode mask and returns te mode as string */
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
 
-char *silc_client_chumode(uint32 mode)
+SILC_TASK_CALLBACK(silc_client_rekey_callback)
 {
-  char string[4];
-
-  if (!mode)
-    return NULL;
-
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "f", 1);
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcClient client = (SilcClient)conn->rekey->context;
+  SilcProtocol protocol;
+  SilcClientRekeyInternalContext *proto_ctx;
 
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "o", 1);
+  SILC_LOG_DEBUG(("Start"));
 
-  return strdup(string);
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = (void *)client;
+  proto_ctx->sock = silc_socket_dup(sock);
+  proto_ctx->responder = FALSE;
+  proto_ctx->pfs = conn->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_REKEY, 
+                     &protocol, proto_ctx, silc_client_rekey_final);
+  sock->protocol = protocol;
+      
+  /* Run the protocol */
+  silc_protocol_execute(protocol, client->timeout_queue, 0, 0);
+
+  /* Re-register re-key timeout */
+  silc_task_register(client->timeout_queue, sock->sock, 
+                    silc_client_rekey_callback,
+                    context, conn->rekey->timeout, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
-/* Parses channel user mode and returns it as special mode character. */
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
 
-char *silc_client_chumode_char(uint32 mode)
+SILC_TASK_CALLBACK(silc_client_rekey_final)
 {
-  char string[4];
-
-  if (!mode)
-    return NULL;
-
-  memset(string, 0, sizeof(string));
-
-  if (mode & SILC_CHANNEL_UMODE_CHANFO)
-    strncat(string, "*", 1);
-
-  if (mode & SILC_CHANNEL_UMODE_CHANOP)
-    strncat(string, "@", 1);
-
-  return strdup(string);
-}
-
-/* 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). */
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx =
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcSocketConnection sock = ctx->sock;
 
-SILC_TASK_CALLBACK_GLOBAL(silc_client_failure_callback)
-{
-  SilcClientFailureContext *f = (SilcClientFailureContext *)context;
+  SILC_LOG_DEBUG(("Start"));
 
-  if (f->sock->protocol) {
-    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-    f->sock->protocol->execute(f->client->timeout_queue, 0,
-                              f->sock->protocol, f->sock->sock, 0, 0);
-    
-    /* Notify application */
-    f->client->ops->failure(f->client, f->sock->user_data, f->sock->protocol,
-                           (void *)f->failure);
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    silc_protocol_cancel(protocol, client->timeout_queue);
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_socket_free(ctx->sock);
+    silc_free(ctx);
+    return;
   }
 
-  silc_free(f);
-}
-
-/* Registers failure timeout to process the received failure packet
-   with timeout. */
-
-void silc_client_process_failure(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
-{
-  SilcClientFailureContext *f;
-  uint32 failure = 0;
-
-  if (sock->protocol) {
-    if (packet->buffer->len >= 4)
-      SILC_GET32_MSB(failure, packet->buffer->data);
-
-    f = silc_calloc(1, sizeof(*f));
-    f->client = client;
-    f->sock = sock;
-    f->failure = failure;
-
-    /* We will wait 5 seconds to process this failure packet */
-    silc_task_register(client->timeout_queue, sock->sock,
-                      silc_client_failure_callback, (void *)f, 5, 0,
-                      SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-  }
+  /* Cleanup */
+  silc_protocol_free(protocol);
+  sock->protocol = NULL;
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_socket_free(ctx->sock);
+  silc_free(ctx);
 }