updates.
[silc.git] / lib / silcclient / client.c
index 089ea87aeee92e53eecb29c5ac529052691f9843..a43baa1146c4afc3cfde1514f0a9133f6d163780 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, 
@@ -179,6 +181,51 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
     }
 }
 
+/* Adds listener socket to the listener sockets table. This function is
+   used to add socket objects that are listeners to the client.  This should
+   not be used to add other connection objects. */
+
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->sockets) {
+    client->sockets = silc_calloc(1, sizeof(*client->sockets));
+    client->sockets[0] = sock;
+    client->sockets_count = 1;
+    return;
+  }
+
+  for (i = 0; i < client->sockets_count; i++) {
+    if (client->sockets[i] == NULL) {
+      client->sockets[i] = sock;
+      return;
+    }
+  }
+
+  client->sockets = silc_realloc(client->sockets, sizeof(*client->sockets) *
+                                (client->sockets_count + 1));
+  client->sockets[client->sockets_count] = sock;
+  client->sockets_count++;
+}
+
+/* Deletes listener socket from the listener sockets table. */
+
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->sockets)
+    return;
+
+  for (i = 0; i < client->sockets_count; i++) {
+    if (client->sockets[i] == sock) {
+      client->sockets[i] = NULL;
+      return;
+    }
+  }
+}
+
 static int 
 silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
 {
@@ -403,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
@@ -508,6 +556,14 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
   conn->remote_id_data_len = SILC_ID_SERVER_LEN;
 
+  /* 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);
@@ -608,21 +664,27 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
       /* If connection is disconnecting already we will finally
         close the connection */
       if (SILC_IS_DISCONNECTING(sock)) {
-       client->ops->disconnect(client, conn);
-       silc_client_close_connection(client, conn);
+       if (sock == conn->sock)
+         client->ops->disconnect(client, conn);
+       silc_client_close_connection(client, sock, conn);
        return;
       }
       
       SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
-      client->ops->disconnect(client, conn);
-      silc_client_close_connection(client, conn);
+      if (sock == conn->sock)
+       client->ops->disconnect(client, conn);
+      silc_client_close_connection(client, sock, conn);
       return;
     }
 
     /* Process the packet. This will call the parser that will then
        decrypt and parse the packet. */
-    silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
-                               silc_client_packet_parse, client);
+    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+      silc_packet_receive_process(sock, conn->receive_key, conn->hmac,
+                                 silc_client_packet_parse, client);
+    else
+      silc_packet_receive_process(sock, NULL, NULL,
+                                 silc_client_packet_parse, client);
   }
 }
 
@@ -643,9 +705,12 @@ static int silc_client_packet_decrypt_check(SilcPacketType packet_type,
 
      all other packets are special packets 
   */
-  if ((packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
-       !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) ||
-      packet_type != SILC_PACKET_CHANNEL_MESSAGE)
+
+  if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
+      (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+    return FALSE;
+
+  if (packet_type != SILC_PACKET_CHANNEL_MESSAGE)
     return TRUE;
 
   return FALSE;
@@ -666,8 +731,13 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
   SILC_LOG_DEBUG(("Start"));
 
   /* Decrypt the received packet */
-  ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
-                           silc_client_packet_decrypt_check, parse_ctx);
+  if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+    ret = silc_packet_decrypt(conn->receive_key, conn->hmac, buffer, packet,
+                             silc_client_packet_decrypt_check, parse_ctx);
+  else
+    ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
+                             silc_client_packet_decrypt_check, parse_ctx);
+
   if (ret < 0)
     goto out;
 
@@ -687,7 +757,7 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
   silc_client_packet_parse_type(client, sock, packet);
 
  out:
-  silc_buffer_clear(sock->inbuf);
+  /*  silc_buffer_clear(sock->inbuf); */
   silc_packet_context_free(packet);
   silc_free(parse_ctx);
 }
@@ -795,7 +865,8 @@ void silc_client_packet_parse_type(SilcClient client,
     break;
 
   case SILC_PACKET_KEY_EXCHANGE:
-    if (sock->protocol) {
+    if (sock->protocol && sock->protocol->protocol && 
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
       SilcClientKEInternalContext *proto_ctx = 
        (SilcClientKEInternalContext *)sock->protocol->context;
 
@@ -818,31 +889,80 @@ void silc_client_packet_parse_type(SilcClient client,
     break;
 
   case SILC_PACKET_KEY_EXCHANGE_1:
-    if (sock->protocol) {
-
+    if (sock->protocol && sock->protocol->protocol && 
+       (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 */
+       sock->protocol->execute(client->timeout_queue, 0, 
+                               sock->protocol, sock->sock, 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 */
+       sock->protocol->execute(client->timeout_queue, 0,
+                               sock->protocol, sock->sock, 0, 0);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
                      "protocol active, packet dropped."));
     }
     break;
   case SILC_PACKET_KEY_EXCHANGE_2:
-    if (sock->protocol) {
-      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);
+    if (sock->protocol && sock->protocol->protocol && 
+       (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 */
+       sock->protocol->execute(client->timeout_queue, 0, 
+                               sock->protocol, sock->sock, 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 */
+       sock->protocol->execute(client->timeout_queue, 0,
+                               sock->protocol, sock->sock, 0, 0);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -885,6 +1005,34 @@ 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 */
+      sock->protocol->execute(client->timeout_queue, 0, 
+                             sock->protocol, sock->sock, 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;
@@ -905,7 +1053,7 @@ void silc_client_packet_send(SilcClient client,
                             SilcCipher cipher,
                             SilcHmac hmac,
                             unsigned char *data, 
-                            unsigned int data_len, 
+                            uint32 data_len, 
                             int force_send)
 {
   SilcPacketContext packetdata;
@@ -929,7 +1077,8 @@ void silc_client_packet_send(SilcClient client,
   /* Set the packet context pointers */
   packetdata.flags = 0;
   packetdata.type = type;
-  if (((SilcClientConnection)sock->user_data)->local_id_data)
+  if (sock->user_data && 
+      ((SilcClientConnection)sock->user_data)->local_id_data)
     packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
   else 
     packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
@@ -979,12 +1128,23 @@ void silc_client_packet_send(SilcClient client,
 }
 
 /* Closes connection to remote end. Free's all allocated data except
-   for some information such as nickname etc. that are valid at all time. */
+   for some information such as nickname etc. that are valid at all time. 
+   If the `sock' is NULL then the conn->sock will be used.  If `sock' is
+   provided it will be checked whether the sock and `conn->sock' are the
+   same (they can be different, ie. a socket can use `conn' as its
+   connection but `conn->sock' might be actually a different connection
+   than the `sock'). */
 
 void silc_client_close_connection(SilcClient client,
+                                 SilcSocketConnection sock,
                                  SilcClientConnection conn)
 {
-  SilcSocketConnection sock = conn->sock;
+  int del = FALSE;
+
+  if (!sock || (sock && conn->sock == sock))
+    del = TRUE;
+  if (!sock)
+    sock = conn->sock;
 
   /* We won't listen for this connection anymore */
   silc_schedule_unset_listen_fd(sock->sock);
@@ -996,13 +1156,13 @@ void silc_client_close_connection(SilcClient client,
   /* Close the actual connection */
   silc_net_close_connection(sock->sock);
 
-  client->ops->say(client, sock->user_data,
-                  "Closed connection to host %s", sock->hostname);
-
   /* Free everything */
-  if (sock->user_data) {
+  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);
@@ -1020,12 +1180,10 @@ void silc_client_close_connection(SilcClient client,
       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->pending_commands)
       silc_dlist_uninit(conn->pending_commands);
+    if (conn->rekey)
+      silc_free(conn->rekey);
 
     conn->sock = NULL;
     conn->remote_port = 0;
@@ -1033,13 +1191,12 @@ void silc_client_close_connection(SilcClient client,
     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;
+    conn->rekey = NULL;
 
     silc_client_del_connection(client, conn);
   }
@@ -1069,7 +1226,7 @@ void silc_client_disconnected_by_server(SilcClient client,
   silc_free(msg);
 
   SILC_SET_DISCONNECTED(sock);
-  silc_client_close_connection(client, sock->user_data);
+  silc_client_close_connection(client, sock, sock->user_data);
 }
 
 /* Received error message from server. Display it on the screen. 
@@ -1145,7 +1302,7 @@ void silc_client_receive_new_id(SilcClient client,
 SilcChannelEntry silc_client_new_channel_id(SilcClient client,
                                            SilcSocketConnection sock,
                                            char *channel_name,
-                                           unsigned int mode, 
+                                           uint32 mode, 
                                            SilcIDPayload idp)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
@@ -1254,7 +1411,7 @@ void silc_client_replace_from_channels(SilcClient client,
 
 /* Parses mode mask and returns the mode as string. */
 
-char *silc_client_chmode(unsigned int mode, SilcChannelEntry channel)
+char *silc_client_chmode(uint32 mode, SilcChannelEntry channel)
 {
   char string[100];
 
@@ -1284,6 +1441,9 @@ char *silc_client_chmode(unsigned int mode, SilcChannelEntry channel)
   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
     strncat(string, "a", 1);
 
+  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
+    strncat(string, "f", 1);
+
   if (mode & SILC_CHANNEL_MODE_CIPHER) {
     char cipher[30];
     memset(cipher, 0, sizeof(cipher));
@@ -1307,7 +1467,7 @@ char *silc_client_chmode(unsigned int mode, SilcChannelEntry channel)
 
 /* Parses channel user mode mask and returns te mode as string */
 
-char *silc_client_chumode(unsigned int mode)
+char *silc_client_chumode(uint32 mode)
 {
   char string[4];
 
@@ -1327,7 +1487,7 @@ char *silc_client_chumode(unsigned int mode)
 
 /* Parses channel user mode and returns it as special mode character. */
 
-char *silc_client_chumode_char(unsigned int mode)
+char *silc_client_chumode_char(uint32 mode)
 {
   char string[4];
 
@@ -1377,7 +1537,7 @@ void silc_client_process_failure(SilcClient client,
                                 SilcPacketContext *packet)
 {
   SilcClientFailureContext *f;
-  unsigned int failure = 0;
+  uint32 failure = 0;
 
   if (sock->protocol) {
     if (packet->buffer->len >= 4)
@@ -1394,3 +1554,84 @@ void silc_client_process_failure(SilcClient client,
                       SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
   }
 }
+
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
+
+SILC_TASK_CALLBACK(silc_client_rekey_callback)
+{
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcClient client = (SilcClient)conn->rekey->context;
+  SilcProtocol protocol;
+  SilcClientRekeyInternalContext *proto_ctx;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* 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 = 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 */
+  protocol->execute(client->timeout_queue, 0, protocol, 
+                   sock->sock, 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);
+}
+
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
+
+SILC_TASK_CALLBACK(silc_client_rekey_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx =
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcSocketConnection sock = ctx->sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    silc_protocol_cancel(client->timeout_queue, protocol);
+    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_free(ctx);
+    return;
+  }
+
+  /* Take the keys into use */
+  if (ctx->pfs == TRUE)
+    silc_client_protocol_rekey_generate_pfs(client, ctx);
+  else
+    silc_client_protocol_rekey_generate(client, ctx);
+
+  /* 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_free(ctx);
+}