udpates.
[silc.git] / lib / silcclient / client.c
index a5b3f8157c0599cd50530c7325e26a5e2e0fcc3f..3e0032f673df36152bd9150b3b43032188ff8fe0 100644 (file)
@@ -30,7 +30,8 @@ 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 bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+                                    void *context);
 static void silc_client_packet_parse_type(SilcClient client, 
                                          SilcSocketConnection sock,
                                          SilcPacketContext *packet);
@@ -182,6 +183,7 @@ SilcClientConnection silc_client_add_connection(SilcClient client,
   conn->remote_port = port;
   conn->context = context;
   conn->pending_commands = silc_dlist_init();
+  conn->ftp_sessions = silc_dlist_init();
 
   /* Add the connection to connections table */
   for (i = 0; i < client->conns_count; i++)
@@ -206,9 +208,16 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 
   for (i = 0; i < client->conns_count; i++)
     if (client->conns[i] == conn) {
+
+      silc_idcache_free(conn->client_cache);
+      silc_idcache_free(conn->channel_cache);
+      silc_idcache_free(conn->server_cache);
       if (conn->pending_commands)
        silc_dlist_uninit(conn->pending_commands);
+      silc_free(conn->remote_host);
+      silc_dlist_uninit(conn->ftp_sessions);
       silc_free(conn);
+
       client->conns[i] = NULL;
     }
 }
@@ -490,7 +499,8 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
                                   ctx->ske->prop->pkcs,
                                   ctx->ske->prop->hash,
                                   ctx->ske->prop->hmac,
-                                  ctx->ske->prop->group);
+                                  ctx->ske->prop->group,
+                                  ctx->responder);
   silc_ske_free_key_material(ctx->keymat);
 
   /* Allocate internal context for the authentication protocol. This
@@ -637,14 +647,17 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
 
 int silc_client_packet_send_real(SilcClient client,
                                 SilcSocketConnection sock,
-                                bool force_send,
-                                bool flush)
+                                bool force_send)
 {
   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))
+  if (SILC_CLIENT_IS_REKEY(sock))
+    force_send = FALSE;
+
+  /* If outbound data is already pending do not force send */
+  if (SILC_IS_OUTBUF_PENDING(sock))
     force_send = FALSE;
 
   /* Send the packet */
@@ -686,18 +699,23 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 
   /* Packet sending */
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    ret = silc_client_packet_send_real(client, sock, TRUE, TRUE);
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
+
+    /* Error */
+    if (ret == -1)
+      return;
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
@@ -712,8 +730,6 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
 
   /* Packet receiving */
   if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
-
     /* Read data from network */
     ret = silc_packet_receive(sock);
     if (ret < 0)
@@ -726,14 +742,14 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
       /* If connection is disconnecting already we will finally
         close the connection */
       if (SILC_IS_DISCONNECTING(sock)) {
-       if (sock == conn->sock)
+       if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
          client->ops->disconnect(client, conn);
        silc_client_close_connection(client, sock, conn);
        return;
       }
       
       SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
-      if (sock == conn->sock)
+      if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
        client->ops->disconnect(client, conn);
       silc_client_close_connection(client, sock, conn);
       return;
@@ -742,42 +758,15 @@ 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_receive,
+      silc_packet_receive_process(sock, FALSE, conn->receive_key, 
+                                 conn->hmac_receive, conn->psn_receive,
                                  silc_client_packet_parse, client);
     else
-      silc_packet_receive_process(sock, NULL, NULL,
+      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, 
                                  silc_client_packet_parse, client);
   }
 }
 
-/* Callback function that the silc_packet_decrypt will call to make the
-   decision whether the packet is normal or special packet. We will 
-   return TRUE if it is normal and FALSE if it is special */
-
-static int silc_client_packet_decrypt_check(SilcPacketType packet_type,
-                                           SilcBuffer buffer,
-                                           SilcPacketContext *packet,
-                                           void *context)
-{
-
-  /* Packet is normal packet, if: 
-
-     1) packet is private message packet and does not have private key set
-     2) is other packet than channel message packet
-
-     all other packets are special packets 
-  */
-
-  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;
-}
-
 /* Parses whole packet, received earlier. */
 
 SILC_TASK_CALLBACK(silc_client_packet_parse_real)
@@ -785,33 +774,17 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
   SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
   SilcClient client = (SilcClient)parse_ctx->context;
   SilcPacketContext *packet = parse_ctx->packet;
-  SilcBuffer buffer = packet->buffer;
   SilcSocketConnection sock = parse_ctx->sock;
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Decrypt the received packet */
-  if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
-    ret = silc_packet_decrypt(conn->receive_key, conn->hmac_receive, 
-                             buffer, packet,
-                             silc_client_packet_decrypt_check, parse_ctx);
+  /* Parse the packet */
+  if (parse_ctx->normal)
+    ret = silc_packet_parse(packet, conn->receive_key);
   else
-    ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
-                             silc_client_packet_decrypt_check, parse_ctx);
-
-  if (ret < 0)
-    goto out;
-
-  if (ret == 0) {
-    /* Parse the packet. Packet type is returned. */
-    ret = silc_packet_parse(packet);
-  } else {
-    /* Parse the packet header in special way as this is "special"
-       packet type. */
-    ret = silc_packet_parse_special(packet);
-  }
+    ret = silc_packet_parse_special(packet, conn->receive_key);
 
   if (ret == SILC_PACKET_NONE)
     goto out;
@@ -828,16 +801,47 @@ SILC_TASK_CALLBACK(silc_client_packet_parse_real)
 /* Parser callback called by silc_packet_receive_process. Thie merely
    registers timeout that will handle the actual parsing when appropriate. */
 
-void silc_client_packet_parse(SilcPacketParserContext *parser_context)
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+                                    void *context)
 {
-  SilcClient client = (SilcClient)parser_context->context;
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock = parser_context->sock;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+
+  if (conn && conn->hmac_receive)
+    conn->psn_receive = parser_context->packet->sequence + 1;
+
+  /* If protocol for this connection is key exchange or rekey then we'll
+     process all packets synchronously, since there might be packets in
+     queue that we are not able to decrypt without first processing the
+     packets before them. */
+  if (sock->protocol && sock->protocol->protocol && 
+      (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
+    silc_client_packet_parse_real(client->schedule, 0, sock->sock,
+                                 parser_context);
+
+    /* Reprocess the buffer since we'll return FALSE. This is because
+       the `conn->receive_key' might have become valid bu processing
+       the previous packet */
+    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+      silc_packet_receive_process(sock, FALSE, conn->receive_key, 
+                                 conn->hmac_receive, conn->psn_receive,
+                                 silc_client_packet_parse, client);
+    else
+      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, 
+                                 silc_client_packet_parse, client);
+    return FALSE;
+  }
 
   /* Parse the packet */
-  silc_schedule_task_add(client->schedule, parser_context->sock->sock, 
+  silc_schedule_task_add(client->schedule, sock->sock, 
                         silc_client_packet_parse_real,
                         (void *)parser_context, 0, 1, 
                         SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
+
+  return TRUE;
 }
 
 /* Parses the packet type and calls what ever routines the packet type
@@ -1100,6 +1104,11 @@ void silc_client_packet_parse_type(SilcClient client,
     silc_client_connection_auth_request(client, sock, packet);
     break;
 
+  case SILC_PACKET_FTP:
+    /* Received file transfer packet. */
+    silc_client_ftp(client, sock, packet);
+    break;
+
   default:
     SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
     break;
@@ -1124,6 +1133,8 @@ void silc_client_packet_send(SilcClient client,
                             int force_send)
 {
   SilcPacketContext packetdata;
+  int block_len;
+  uint32 sequence = 0;
 
   if (!sock)
     return;
@@ -1142,8 +1153,13 @@ void silc_client_packet_send(SilcClient client,
       dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
       dst_id_type = SILC_ID_SERVER;
     }
+
+    if (hmac)
+      sequence = ((SilcClientConnection)sock->user_data)->psn_send++;
   }
 
+  block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+
   /* Set the packet context pointers */
   packetdata.flags = 0;
   packetdata.type = type;
@@ -1169,7 +1185,7 @@ void silc_client_packet_send(SilcClient client,
   }
   packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
 
   /* Prepare outgoing data buffer for packet sending */
   silc_packet_send_prepare(sock, 
@@ -1188,17 +1204,34 @@ void silc_client_packet_send(SilcClient client,
     silc_buffer_put(sock->outbuf, data, data_len);
 
   /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
+  silc_packet_assemble(&packetdata, cipher);
 
   /* Encrypt the packet */
   if (cipher)
-    silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, 
+                       sock->outbuf->len);
 
-  SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len),
+  SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, sock->outbuf->len),
                   sock->outbuf->data, sock->outbuf->len);
 
   /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send, FALSE);
+  silc_client_packet_send_real(client, sock, force_send);
+}
+
+void silc_client_packet_queue_purge(SilcClient client,
+                                   SilcSocketConnection sock)
+{
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
 }
 
 /* Closes connection to remote end. Free's all allocated data except
@@ -1299,13 +1332,23 @@ 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_send)       /* conn->hmac_receive is same */
+    if (conn->hmac_send)
       silc_hmac_free(conn->hmac_send);
+    if (conn->hmac_receive)
+      silc_hmac_free(conn->hmac_receive);
     if (conn->pending_commands)
       silc_dlist_uninit(conn->pending_commands);
     if (conn->rekey)
       silc_free(conn->rekey);
 
+    if (conn->active_session) {
+      sock->user_data = NULL;
+      silc_client_ftp_session_free(conn->active_session);
+      conn->active_session = NULL;
+    }
+
+    silc_client_ftp_free_sessions(client, conn);
+
     memset(conn, 0, sizeof(*conn));
     silc_client_del_connection(client, conn);
   }
@@ -1601,6 +1644,10 @@ SILC_TASK_CALLBACK(silc_client_rekey_final)
     return;
   }
 
+  /* Purge the outgoing data queue to assure that all rekey packets really
+     go to the network before we quit the protocol. */
+  silc_client_packet_queue_purge(client, sock);
+
   /* Cleanup */
   silc_protocol_free(protocol);
   sock->protocol = NULL;