Fixed various connect aborting crashes.
[silc.git] / lib / silcclient / client.c
index f52ff134d72d7d6223284e02c7480d6aaa6a5f44..932009862bf91dd0179bd14d168bafb311ef164d 100644 (file)
@@ -180,11 +180,16 @@ static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
 
   /* Connection callback will not be called after user aborted connecting */
   conn->callback = NULL;
+  conn->internal->cop = NULL;
 
   /* Signal to close connection */
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
-    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+
+    /* If user aborts before connection machine is even up yet, then don't
+       send signal yet.  It will process this event when it comes up. */
+    if (silc_fsm_is_started(&conn->internal->fsm))
+      SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   }
 }
 
@@ -208,7 +213,9 @@ SILC_FSM_STATE(silc_client_connection_st_start)
   silc_fsm_event_init(&conn->internal->wait_event, connfsm);
   silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
 
-  /* Schedule any events set in initialization */
+  /* Schedule any events possibly set in initialization */
+  if (conn->internal->disconnected)
+    SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   if (conn->internal->connect)
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
   if (conn->internal->key_exchange)
@@ -233,9 +240,17 @@ SILC_FSM_STATE(silc_client_connection_st_run)
   /* Process events */
   thread = &conn->internal->event_thread;
 
+  if (conn->internal->disconnected) {
+    /** Event: disconnected */
+    SILC_LOG_DEBUG(("Event: disconnected"));
+    silc_fsm_next(fsm, silc_client_connection_st_close);
+    return SILC_FSM_YIELD;
+  }
+
   if (conn->internal->connect) {
     SILC_LOG_DEBUG(("Event: connect"));
     conn->internal->connect = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: connect */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
@@ -247,6 +262,7 @@ SILC_FSM_STATE(silc_client_connection_st_run)
   if (conn->internal->key_exchange) {
     SILC_LOG_DEBUG(("Event: key exchange"));
     conn->internal->key_exchange = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: key exchange */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
@@ -258,6 +274,7 @@ SILC_FSM_STATE(silc_client_connection_st_run)
   if (conn->internal->rekeying) {
     SILC_LOG_DEBUG(("Event: rekey"));
     conn->internal->rekeying = FALSE;
+    SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
 
     /*** Event: rekey */
     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
@@ -266,13 +283,6 @@ SILC_FSM_STATE(silc_client_connection_st_run)
     return SILC_FSM_CONTINUE;
   }
 
-  if (conn->internal->disconnected) {
-    /** Event: disconnected */
-    SILC_LOG_DEBUG(("Event: disconnected"));
-    silc_fsm_next(fsm, silc_client_connection_st_close);
-    return SILC_FSM_YIELD;
-  }
-
   /* NOT REACHED */
   SILC_ASSERT(FALSE);
   return SILC_FSM_CONTINUE;
@@ -302,7 +312,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
 
   case SILC_PACKET_FTP:
     /* File transfer packet */
-//    silc_fsm_next(fsm, silc_client_ftp);
+    silc_fsm_next(fsm, silc_client_ftp);
     break;
 
   case SILC_PACKET_CHANNEL_KEY:
@@ -377,7 +387,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
 /* Disconnection event to close remote connection.  We close the connection
    and finish the connection machine in this state.  The connection context
    is deleted in the machine destructor.  The connection callback is called
-   in this state if it set. */
+   in this state if it is set. */
 
 SILC_FSM_STATE(silc_client_connection_st_close)
 {
@@ -424,8 +434,9 @@ SILC_FSM_STATE(silc_client_connection_st_close)
 
   SILC_LOG_DEBUG(("Closing remote connection"));
 
-  /* Close connection */
-  silc_packet_stream_destroy(conn->stream);
+  /* Close connection. */
+  if (conn->stream)
+    silc_packet_stream_destroy(conn->stream);
 
   SILC_LOG_DEBUG(("Finishing connection machine"));
   return SILC_FSM_FINISH;
@@ -561,9 +572,10 @@ SILC_FSM_STATE(silc_client_st_stop)
 
 /* Adds new connection.  Creates the connection context and returns it. */
 
-static SilcClientConnection
+SilcClientConnection
 silc_client_add_connection(SilcClient client,
                           SilcConnectionType conn_type,
+                          SilcBool connect,
                           SilcClientConnectionParams *params,
                           SilcPublicKey public_key,
                           SilcPrivateKey private_key,
@@ -623,31 +635,33 @@ silc_client_add_connection(SilcClient client,
   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
 
   /* Allocate client, channel and serve caches */
-  conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
-                                                   NULL, NULL);
-  conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
-                                                    NULL, NULL);
-  conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
-                                                   NULL, NULL);
-  if (!conn->internal->client_cache || !conn->internal->channel_cache ||
-      !conn->internal->server_cache) {
-    silc_client_del_connection(client, conn);
-    return NULL;
+  if (conn_type != SILC_CONN_CLIENT) {
+    conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
+                                                     NULL, NULL);
+    conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
+                                                      NULL, NULL);
+    conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
+                                                     NULL, NULL);
+    if (!conn->internal->client_cache || !conn->internal->channel_cache ||
+       !conn->internal->server_cache) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
   }
 
-  //  conn->internal->ftp_sessions = silc_dlist_init();
-
-  /* Initialize our async operation so that application may abort us
-     while we're connecting. */
-  conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
-                                        NULL, conn);
-  if (!conn->internal->cop) {
-    silc_client_del_connection(client, conn);
-    return NULL;
+  if (connect) {
+    /* Initialize our async operation so that application may abort us
+       while we're connecting. */
+    conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
+                                          NULL, conn);
+    if (!conn->internal->cop) {
+      silc_client_del_connection(client, conn);
+      return NULL;
+    }
   }
 
-  /* Run the connection state machine.  If threads are in use the machine
-     is always run in a real thread. */
+  /* Run the connection state machine.  If threads are in use the connection
+     machine is always run in a real thread. */
   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
                                 silc_client_connection_finished, NULL,
                                 client->internal->params->threads);
@@ -678,22 +692,28 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
 
   /* Free all cache entries */
-  if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list)))
-      silc_client_del_server(client, conn, entry->context);
+  if (conn->internal->server_cache) {
+    if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
+       silc_client_del_server(client, conn, entry->context);
+    }
   }
-  if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list))) {
-      silc_client_empty_channel(client, conn, entry->context);
-      silc_client_del_channel(client, conn, entry->context);
+  if (conn->internal->channel_cache) {
+    if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list))) {
+       silc_client_empty_channel(client, conn, entry->context);
+       silc_client_del_channel(client, conn, entry->context);
+      }
     }
   }
-  if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
-    silc_list_start(list);
-    while ((entry = silc_list_get(list)))
-      silc_client_del_client(client, conn, entry->context);
+  if (conn->internal->client_cache) {
+    if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
+      silc_list_start(list);
+      while ((entry = silc_list_get(list)))
+       silc_client_del_client(client, conn, entry->context);
+    }
   }
 
   /* Free ID caches */
@@ -718,6 +738,11 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
   if (conn->internal->sha1hash)
     silc_hash_free(conn->internal->sha1hash);
   silc_atomic_uninit16(&conn->internal->cmd_ident);
+  silc_free(conn->internal->away_message);
+  if (conn->internal->rekey)
+    silc_ske_free_rekey_material(conn->internal->rekey);
+  if (conn->internal->cop)
+    silc_async_free(conn->internal->cop);
 
   silc_free(conn->internal);
   memset(conn, 'F', sizeof(*conn));
@@ -747,7 +772,7 @@ silc_client_connect_to_server(SilcClient client,
     return NULL;
 
   /* Add new connection */
-  conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
+  conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
@@ -783,8 +808,11 @@ silc_client_connect_to_client(SilcClient client,
   if (!client || !remote_host)
     return NULL;
 
+  if (params)
+    params->no_authentication = TRUE;
+
   /* Add new connection */
-  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
+  conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
                                    public_key, private_key, remote_host,
                                    port, callback, context);
   if (!conn) {
@@ -826,14 +854,14 @@ silc_client_key_exchange(SilcClient client,
   }
 
   /* Add new connection */
-  conn = silc_client_add_connection(client, conn_type, params,
+  conn = silc_client_add_connection(client, conn_type, TRUE, params,
                                    public_key, private_key,
                                    (char *)host, port, callback, context);
   if (!conn) {
     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
     return NULL;
   }
-  conn->stream = (void *)stream;
+  conn->internal->user_stream = stream;
 
   /* Signal connection to start key exchange */
   conn->internal->key_exchange = TRUE;
@@ -848,6 +876,7 @@ void silc_client_close_connection(SilcClient client,
   SILC_LOG_DEBUG(("Closing connection %p", conn));
 
   /* Signal to close connection */
+  conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
   if (!conn->internal->disconnected) {
     conn->internal->disconnected = TRUE;
     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
@@ -911,7 +940,10 @@ void silc_client_free(SilcClient client)
     silc_hmac_unregister_all();
   }
 
+  silc_packet_engine_stop(client->internal->packet_engine);
+  silc_dlist_uninit(client->internal->ftp_sessions);
   silc_atomic_uninit16(&client->internal->conns);
+  silc_mutex_free(client->internal->lock);
   silc_free(client->username);
   silc_free(client->hostname);
   silc_free(client->realname);
@@ -968,6 +1000,10 @@ SilcBool silc_client_init(SilcClient client, const char *username,
   if (!username || !hostname || !realname)
     return FALSE;
 
+  client->internal->ftp_sessions = silc_dlist_init();
+  if (!client->internal->ftp_sessions)
+    return FALSE;
+
   if (!client->internal->params->dont_register_crypto_library) {
     /* Initialize the crypto library.  If application has done this already
        this has no effect.  Also, we will not be overriding something