updates.
[silc.git] / lib / silcclient / client.c
index a43baa1146c4afc3cfde1514f0a9133f6d163780..f870a2a35d1dc7b773d5488e8c3eda5ac3203808 100644 (file)
@@ -191,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++;
 }
 
@@ -220,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;
     }
@@ -319,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;
@@ -434,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);
@@ -521,8 +521,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);
@@ -564,13 +563,12 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
                     (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;
 }
@@ -581,10 +579,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)
@@ -630,7 +634,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. */
@@ -680,7 +684,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,
@@ -732,7 +736,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,
@@ -776,7 +781,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. */
 
@@ -1023,10 +1028,15 @@ void silc_client_packet_parse_type(SilcClient client,
        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);
+      if (proto_ctx->responder == FALSE)
+       sock->protocol->execute(client->timeout_queue, 0, 
+                               sock->protocol, sock->sock, 0, 0);
+      else
+       /* 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."));
@@ -1065,8 +1075,105 @@ 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;
+      dst_id_type = SILC_ID_SERVER;
+    }
+  }
+
+  /* Set the packet context pointers */
+  packetdata.flags = 0;
+  packetdata.type = type;
+  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));
+  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_type = dst_id_type;
+  } else {
+    packetdata.dst_id = NULL;
+    packetdata.dst_id_len = 0;
+    packetdata.dst_id_type = SILC_ID_NONE;
+  }
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          data_len);
+
+  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the data to the buffer */
+  if (data && data_len)
+    silc_buffer_put(sock->outbuf, data, data_len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata);
+
+  /* Encrypt the packet */
+  if (cipher)
+    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+
+  SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_client_packet_send_real(client, sock, force_send, FALSE);
+}
+
+void silc_client_packet_send_flush(SilcClient client, 
+                                  SilcSocketConnection sock,
+                                  SilcPacketType type, 
+                                  void *dst_id,
+                                  SilcIdType dst_id_type,
+                                  SilcCipher cipher,
+                                  SilcHmac hmac,
+                                  unsigned char *data, 
+                                  uint32 data_len)
+{
+  SilcPacketContext packetdata;
+
+  /* First flush the packet queue. */
+  
+  if (sock->outbuf->data - sock->outbuf->head)
+    silc_buffer_push(sock->outbuf, 
+                    sock->outbuf->data - sock->outbuf->head);
+  
+  silc_client_packet_send_real(client, sock, TRUE, TRUE);
+  
+  /* 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 
+     available for this connection it will be set for output as well. 
+     This call clears the output setting and sets it only for input. */
+  SILC_CLIENT_SET_CONNECTION_FOR_INPUT(sock->sock);
+  SILC_UNSET_OUTBUF_PENDING(sock);
+  silc_buffer_clear(sock->outbuf);
+
+  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+
+  /* Get data used in the packet sending, keys and stuff */
+  if ((!cipher || !hmac || !dst_id) && sock->user_data) {
+    if (!cipher && ((SilcClientConnection)sock->user_data)->send_key)
+      cipher = ((SilcClientConnection)sock->user_data)->send_key;
+
+    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;
@@ -1124,7 +1231,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, TRUE, TRUE);
 }
 
 /* Closes connection to remote end. Free's all allocated data except
@@ -1178,8 +1285,8 @@ 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_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)
@@ -1190,7 +1297,8 @@ void silc_client_close_connection(SilcClient client,
     conn->remote_type = 0;
     conn->send_key = NULL;
     conn->receive_key = NULL;
-    conn->hmac = NULL;
+    conn->hmac_send = NULL;
+    conn->hmac_receive = NULL;
     conn->local_id = NULL;
     conn->local_id_data = NULL;
     conn->remote_host = NULL;
@@ -1505,30 +1613,6 @@ char *silc_client_chumode_char(uint32 mode)
   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). */
-
-SILC_TASK_CALLBACK_GLOBAL(silc_client_failure_callback)
-{
-  SilcClientFailureContext *f = (SilcClientFailureContext *)context;
-
-  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);
-  }
-
-  silc_free(f);
-}
-
 /* Registers failure timeout to process the received failure packet
    with timeout. */
 
@@ -1536,22 +1620,15 @@ 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);
+    /* Notify application */
+    client->ops->failure(client, sock->user_data, sock->protocol,
+                        (void *)failure);
   }
 }
 
@@ -1572,7 +1649,7 @@ SILC_TASK_CALLBACK(silc_client_rekey_callback)
      to the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->client = (void *)client;
-  proto_ctx->sock = sock;
+  proto_ctx->sock = silc_socket_dup(sock);
   proto_ctx->responder = FALSE;
   proto_ctx->pfs = conn->rekey->pfs;
       
@@ -1616,16 +1693,11 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
+    silc_socket_free(ctx->sock);
     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;
@@ -1633,5 +1705,6 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  silc_socket_free(ctx->sock);
   silc_free(ctx);
 }