updates.
[silc.git] / lib / silcclient / client.c
index c7c175911076778508c36df14a98e2f1a76620a9..305e9c7df5fed223785f7a7e3264285b904c0129 100644 (file)
 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 bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
                                     void *context);
 static void silc_client_packet_parse_type(SilcClient client, 
                                          SilcSocketConnection sock,
@@ -183,6 +182,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++)
@@ -207,9 +207,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;
     }
 }
@@ -390,6 +397,19 @@ bool silc_client_start_key_exchange(SilcClient client,
   return TRUE;
 }
 
+/* Callback called when error has occurred during connecting to the server.
+   The `connect' client operation will be called. */
+
+SILC_TASK_CALLBACK(silc_client_connect_failure)
+{
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)context;
+  SilcClient client = (SilcClient)ctx->client;
+
+  client->ops->connect(client, ctx->sock->user_data, FALSE);
+  silc_free(ctx);
+}
+
 /* Start of the connection to the remote server. This is called after
    succesful TCP/IP connection has been established to the remote host. */
 
@@ -478,8 +498,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
     silc_socket_free(ctx->sock);
 
     /* Notify application of failure */
-    client->ops->connect(client, ctx->sock->user_data, FALSE);
-    silc_free(ctx);
+    silc_schedule_task_add(client->schedule, ctx->sock->sock,
+                          silc_client_connect_failure, ctx,
+                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -491,7 +512,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
@@ -583,8 +605,9 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
     silc_socket_free(ctx->sock);
 
     /* Notify application of failure */
-    client->ops->connect(client, ctx->sock->user_data, FALSE);
-    silc_free(ctx);
+    silc_schedule_task_add(client->schedule, ctx->sock->sock,
+                          silc_client_connect_failure, ctx,
+                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
     return;
   }
 
@@ -733,14 +756,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;
@@ -758,56 +781,66 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
   }
 }
 
-/* Parses whole packet, received earlier. */
+/* Parser callback called by silc_packet_receive_process. Thie merely
+   registers timeout that will handle the actual parsing when appropriate. */
 
-SILC_TASK_CALLBACK(silc_client_packet_parse_real)
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+                                    void *context)
 {
-  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
-  SilcClient client = (SilcClient)parse_ctx->context;
-  SilcPacketContext *packet = parse_ctx->packet;
-  SilcSocketConnection sock = parse_ctx->sock;
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock = parser_context->sock;
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  int ret;
+  SilcPacketContext *packet = parser_context->packet;
+  SilcPacketType ret;
 
-  SILC_LOG_DEBUG(("Start"));
+  if (conn && conn->hmac_receive && conn->sock == sock)
+    conn->psn_receive = parser_context->packet->sequence + 1;
 
-  /* Parse the packet */
-  if (parse_ctx->normal)
+  /* Parse the packet immediately */
+  if (parser_context->normal)
     ret = silc_packet_parse(packet, conn->receive_key);
   else
     ret = silc_packet_parse_special(packet, conn->receive_key);
 
-  if (ret == SILC_PACKET_NONE)
-    goto out;
+  if (ret == SILC_PACKET_NONE) {
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
+    return FALSE;
+  }
+  
+  /* 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 ((ret == SILC_PACKET_REKEY || ret == SILC_PACKET_REKEY_DONE) ||
+      (sock->protocol && sock->protocol->protocol && 
+       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY))) {
+
+    /* Parse the incoming packet type */
+    silc_client_packet_parse_type(client, sock, packet);
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
+
+    /* Reprocess the buffer since we'll return FALSE. This is because
+       the `conn->receive_key' might have become valid by 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 incoming packet type */
   silc_client_packet_parse_type(client, sock, packet);
-
- out:
-  /*  silc_buffer_clear(sock->inbuf); */
   silc_packet_context_free(packet);
-  silc_free(parse_ctx);
-}
-
-/* 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,
-                             void *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;
-
-  /* Parse the packet */
-  silc_schedule_task_add(client->schedule, parser_context->sock->sock, 
-                        silc_client_packet_parse_real,
-                        (void *)parser_context, 0, 1, 
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+  silc_free(parser_context);
+  return TRUE;
 }
 
 /* Parses the packet type and calls what ever routines the packet type
@@ -1177,7 +1210,7 @@ void silc_client_packet_send(SilcClient client,
     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 */
@@ -1214,6 +1247,8 @@ void silc_client_close_connection(SilcClient client,
 {
   int del = FALSE;
 
+  SILC_LOG_DEBUG(("Start"));
+
   if (!sock || (sock && conn->sock == sock))
     del = TRUE;
   if (!sock)
@@ -1307,6 +1342,14 @@ void silc_client_close_connection(SilcClient client,
     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);
   }
@@ -1314,6 +1357,22 @@ void silc_client_close_connection(SilcClient client,
   silc_socket_free(sock);
 }
 
+/* Called when we receive disconnection packet from server. This 
+   closes our end properly and displays the reason of the disconnection
+   on the screen. */
+
+SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later)
+{
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock;
+
+  SILC_CLIENT_GET_SOCK(client, fd, sock);
+  if (sock == NULL)
+    return;
+
+  silc_client_close_connection(client, sock, sock->user_data);
+}
+
 /* Called when we receive disconnection packet from server. This 
    closes our end properly and displays the reason of the disconnection
    on the screen. */
@@ -1332,7 +1391,12 @@ void silc_client_disconnected_by_server(SilcClient client,
   silc_free(msg);
 
   SILC_SET_DISCONNECTED(sock);
-  silc_client_close_connection(client, sock, sock->user_data);
+
+  /* Close connection through scheduler. */
+  silc_schedule_task_add(client->schedule, sock->sock, 
+                        silc_client_disconnected_by_server_later,
+                        client, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
 }
 
 /* Received error message from server. Display it on the screen. 
@@ -1359,6 +1423,7 @@ void silc_client_receive_new_id(SilcClient client,
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   int connecting = FALSE;
+  SilcClientID *client_id = silc_id_payload_get_id(idp);
   SilcBuffer sidp;
 
   if (!conn->local_entry)
@@ -1366,6 +1431,12 @@ void silc_client_receive_new_id(SilcClient client,
 
   /* Delete old ID from ID cache */
   if (conn->local_id) {
+    /* Check whether they are different */
+    if (SILC_ID_CLIENT_COMPARE(conn->local_id, client_id)) {
+      silc_free(client_id);
+      return;
+    }
+
     silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
     silc_free(conn->local_id);
   }
@@ -1375,7 +1446,7 @@ void silc_client_receive_new_id(SilcClient client,
   if (conn->local_id_data)
     silc_free(conn->local_id_data);
 
-  conn->local_id = silc_id_payload_get_id(idp);
+  conn->local_id = client_id;
   conn->local_id_data = silc_id_payload_get_data(idp);
   conn->local_id_data_len = silc_id_payload_get_len(idp);;
 
@@ -1395,17 +1466,18 @@ void silc_client_receive_new_id(SilcClient client,
   silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id, 
                   (void *)conn->local_entry, FALSE);
 
-  /* Issue INFO command to fetch the real server name and server information
-     and other stuff. */
-  sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
-  silc_client_send_command(client, conn, SILC_COMMAND_INFO,
-                          ++conn->cmd_ident, 1, 2, sidp->data, sidp->len);
-  silc_buffer_free(sidp);
+  if (connecting) {
+    /* Issue INFO comqmand to fetch the real server name and server information
+       and other stuff. */
+    sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
+    silc_client_send_command(client, conn, SILC_COMMAND_INFO,
+                            ++conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+    silc_buffer_free(sidp);
 
-  /* Notify application of successful connection. We do it here now that
-     we've received the Client ID and are allowed to send traffic. */
-  if (connecting)
+    /* Notify application of successful connection. We do it here now that
+       we've received the Client ID and are allowed to send traffic. */
     client->ops->connect(client, conn, TRUE);
+  }
 }
 
 /* Processed received Channel ID for a channel. This is called when client
@@ -1720,31 +1792,3 @@ silc_client_request_authentication_method(SilcClient client,
                           conn, client->params->connauth_request_secs, 0,
                           SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
-
-/* Called when file transfer packet is received. This will parse the
-   packet and give it to the file transfer protocol. */
-
-void silc_client_ftp(SilcClient client,
-                    SilcSocketConnection sock,
-                    SilcPacketContext *packet)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  uint8 type;
-  int ret;
-
-  /* Parse the payload */
-  ret = silc_buffer_unformat(packet->buffer,
-                            SILC_STR_UI_CHAR(&type),
-                            SILC_STR_END);
-  if (ret == -1)
-    return;
-
-  /* We support only type number 1 (== SFTP) */
-  if (type != 1)
-    return;
-
-  silc_buffer_pull(packet->buffer, 1);
-
-  /* Give it to the file transfer protocol processor. */
-  //silc_sftp_client_receive_process(xxx, sock, packet);
-}