More client library rewrites (key agreement, plus other).
[silc.git] / lib / silcclient / client.c
index 2d9e08a4f205363a43be5efc1c4282c51190d1fc..9354d28d6c6e3b641a94dc0fe396cb2dd2477e4c 100644 (file)
@@ -281,8 +281,8 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
     break;
 
   case SILC_PACKET_KEY_AGREEMENT:
-    /* Key agreement */
-    //    silc_client_key_agreement(client, conn, packet);
+    /** Key agreement */
+    silc_fsm_next(fsm, silc_client_key_agreement);
     break;
 
   case SILC_PACKET_COMMAND:
@@ -293,6 +293,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet)
   case SILC_PACKET_NEW_ID:
     /** New ID */
     silc_fsm_next(fsm, silc_client_new_id);
+    break;
 
   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
     /* Reply to connection authentication request to resolve authentication
@@ -320,7 +321,9 @@ SILC_FSM_STATE(silc_client_connection_st_close)
 
   SILC_LOG_DEBUG(("Closing remote connection"));
 
-  /* XXX abort any ongoing events (protocols) */
+  /* Abort ongoing events */
+  if (conn->internal->op)
+    silc_async_abort(conn->internal->op, NULL, NULL);
 
   /* Close connection */
   silc_packet_stream_destroy(conn->stream);
@@ -379,14 +382,16 @@ SILC_FSM_STATE(silc_client_disconnect)
 
   /* Call connection callback */
   conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
-                message, conn->context);
+                message, conn->callback_context);
 
   silc_free(message);
   silc_packet_free(packet);
 
   /* Signal to close connection */
-  conn->internal->disconnected = TRUE;
-  SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
 
   return SILC_FSM_FINISH;
 }
@@ -406,6 +411,7 @@ SILC_FSM_STATE(silc_client_st_run)
 
   if (client->internal->run_callback && client->internal->ops->running) {
     /* Call running callbcak back to application */
+    SILC_LOG_DEBUG(("We are running, call running callback"));
     client->internal->run_callback = FALSE;
     client->internal->ops->running(client, client->application);
     return SILC_FSM_CONTINUE;
@@ -441,11 +447,6 @@ silc_client_add_connection(SilcClient client,
   conn = silc_calloc(1, sizeof(*conn));
   if (!conn)
     return NULL;
-  conn->internal = silc_calloc(1, sizeof(*conn->internal));
-  if (!conn->internal) {
-    silc_free(conn);
-    return NULL;
-  }
 
   conn->client = client;
   conn->public_key = public_key;
@@ -454,7 +455,24 @@ silc_client_add_connection(SilcClient client,
   conn->remote_port = port ? port : 706;
   conn->type = conn_type;
   conn->callback = callback;
-  conn->context = context;
+  conn->callback_context = context;
+
+  conn->internal = silc_calloc(1, sizeof(*conn->internal));
+  if (!conn->internal) {
+    silc_free(conn);
+    return NULL;
+  }
+  conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
+  silc_mutex_alloc(&conn->internal->lock);
+  silc_atomic_init16(&conn->internal->cmd_ident, 0);
+
+  if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
+    silc_free(conn);
+    silc_free(conn->internal);
+    return NULL;
+  }
+  if (params)
+    conn->internal->params = *params;
   conn->internal->verbose = TRUE;
   silc_list_init(conn->internal->pending_commands,
                 struct SilcClientCommandContextStruct, next);
@@ -474,14 +492,6 @@ silc_client_add_connection(SilcClient client,
 
   conn->internal->ftp_sessions = silc_dlist_init();
 
-  if (params) {
-    if (params->detach_data)
-      conn->internal->params.detach_data =
-       silc_memdup(params->detach_data,
-                   params->detach_data_len);
-    conn->internal->params.detach_data_len = params->detach_data_len;
-  }
-
   /* Run the connection state machine.  If threads are in use the machine
      is always run in a real thread. */
   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
@@ -496,96 +506,66 @@ silc_client_add_connection(SilcClient client,
   return conn;
 }
 
-/* Removes connection from client. Frees all memory. */
+/* Deletes connection.  This is always called from the connection machine
+   destructor.  Do not call this directly other places. */
 
 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
 {
-#if 0
-  SilcClientConnection c;
-  SilcIDCacheList list;
+  SilcList list;
   SilcIDCacheEntry entry;
-  SilcClientCommandPending *r;
-  SilcBool ret;
-
-  silc_dlist_start(client->internal->conns);
-  while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
-    if (c != conn)
-      continue;
-
-    /* Free all cache entries */
-    if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
-      ret = silc_idcache_list_first(list, &entry);
-      while (ret) {
-       silc_client_del_client(client, conn, entry->context);
-       ret = silc_idcache_list_next(list, &entry);
-      }
-      silc_idcache_list_free(list);
-    }
-
-    if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
-      ret = silc_idcache_list_first(list, &entry);
-      while (ret) {
-       silc_client_del_channel(client, conn, entry->context);
-       ret = silc_idcache_list_next(list, &entry);
-      }
-      silc_idcache_list_free(list);
-    }
-
-    if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
-      ret = silc_idcache_list_first(list, &entry);
-      while (ret) {
-       silc_client_del_server(client, conn, entry->context);
-       ret = silc_idcache_list_next(list, &entry);
-      }
-      silc_idcache_list_free(list);
-    }
-
-    /* Clear ID caches */
-    if (conn->internal->client_cache)
-      silc_idcache_free(conn->internal->client_cache);
-    if (conn->internal->channel_cache)
-      silc_idcache_free(conn->internal->channel_cache);
-    if (conn->internal->server_cache)
-      silc_idcache_free(conn->internal->server_cache);
-
-    /* Free data (my ID is freed in above silc_client_del_client).
-       conn->nickname is freed when freeing the local_entry->nickname. */
-    silc_free(conn->remote_host);
-    silc_free(conn->local_id_data);
-    if (conn->internal->send_key)
-      silc_cipher_free(conn->internal->send_key);
-    if (conn->internal->receive_key)
-      silc_cipher_free(conn->internal->receive_key);
-    if (conn->internal->hmac_send)
-      silc_hmac_free(conn->internal->hmac_send);
-    if (conn->internal->hmac_receive)
-      silc_hmac_free(conn->internal->hmac_receive);
-    silc_free(conn->internal->rekey);
-
-    if (conn->internal->active_session) {
-      if (conn->sock)
-       conn->sock->user_data = NULL;
-      silc_client_ftp_session_free(conn->internal->active_session);
-      conn->internal->active_session = NULL;
-    }
-
-    silc_client_ftp_free_sessions(client, conn);
-
-    if (conn->internal->pending_commands) {
-      silc_dlist_start(conn->internal->pending_commands);
-      while ((r = silc_dlist_get(conn->internal->pending_commands))
-            != SILC_LIST_END)
-       silc_dlist_del(conn->internal->pending_commands, r);
-      silc_dlist_uninit(conn->internal->pending_commands);
-    }
+  SilcFSMThread thread;
+  SilcClientCommandContext cmd;
 
-    silc_free(conn->internal);
-    memset(conn, 0, sizeof(*conn));
-    silc_free(conn);
+  SILC_LOG_DEBUG(("Freeing connection %p", conn));
 
-    silc_dlist_del(client->internal->conns, conn);
+  /* Free all cache entries */
+  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);
   }
-#endif /* 0 */
+  if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
+    silc_list_start(list);
+    while ((entry = silc_list_get(list)))
+      silc_client_del_channel(client, conn, entry->context);
+  }
+  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);
+  }
+
+  /* Free ID caches */
+  if (conn->internal->client_cache)
+    silc_idcache_free(conn->internal->client_cache);
+  if (conn->internal->channel_cache)
+    silc_idcache_free(conn->internal->channel_cache);
+  if (conn->internal->server_cache)
+    silc_idcache_free(conn->internal->server_cache);
+
+  /* Free thread pool */
+  silc_list_start(conn->internal->thread_pool);
+  while ((thread = silc_list_get(conn->internal->thread_pool)))
+    silc_fsm_free(thread);
+
+  /* Free pending commands */
+  silc_list_start(conn->internal->pending_commands);
+  while ((cmd = silc_list_get(conn->internal->pending_commands)))
+    silc_client_command_free(cmd);
+
+  silc_free(conn->remote_host);
+  silc_buffer_free(conn->internal->local_idp);
+  silc_buffer_free(conn->internal->remote_idp);
+  silc_mutex_free(conn->internal->lock);
+  if (conn->internal->hash)
+    silc_hash_free(conn->internal->hash);
+  if (conn->internal->sha1hash)
+    silc_hash_free(conn->internal->sha1hash);
+  silc_atomic_uninit16(&conn->internal->cmd_ident);
+
+  silc_free(conn->internal);
+  memset(conn, 'F', sizeof(*conn));
+  silc_free(conn);
 }
 
 
@@ -651,10 +631,6 @@ SilcBool silc_client_connect_to_client(SilcClient client,
     return FALSE;
   }
 
-  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                            "Connecting to port %d of client host %s",
-                            port, remote_host);
-
   /* Signal connection machine to start connecting */
   conn->internal->connect = TRUE;
   return TRUE;
@@ -679,8 +655,11 @@ SilcBool silc_client_key_exchange(SilcClient client,
   if (!client || !stream)
     return FALSE;
 
-  if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port))
+  if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
+    SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
+    callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
     return FALSE;
+  }
 
   /* Add new connection */
   conn = silc_client_add_connection(client, conn_type, params,
@@ -697,6 +676,20 @@ SilcBool silc_client_key_exchange(SilcClient client,
   return TRUE;
 }
 
+/* Closes remote connection */
+
+void silc_client_close_connection(SilcClient client,
+                                 SilcClientConnection conn)
+{
+  SILC_LOG_DEBUG(("Closing connection %p", conn));
+
+  /* Signal to close connection */
+  if (!conn->internal->disconnected) {
+    conn->internal->disconnected = TRUE;
+    SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+  }
+}
+
 #if 0
 /* Finalizes the connection to the remote SILC server. This is called
    after authentication protocol has been completed. This send our
@@ -1000,9 +993,6 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
   if (params)
     memcpy(new_client->internal->params, params, sizeof(*params));
 
-  if (!new_client->internal->params->task_max)
-    new_client->internal->params->task_max = 200;
-
   if (!new_client->internal->params->rekey_secs)
     new_client->internal->params->rekey_secs = 3600;
 
@@ -1020,63 +1010,71 @@ SilcClient silc_client_alloc(SilcClientOperations *ops,
 
 void silc_client_free(SilcClient client)
 {
-  if (client) {
-    if (client->rng)
-      silc_rng_free(client->rng);
-
-    if (!client->internal->params->dont_register_crypto_library) {
-      silc_cipher_unregister_all();
-      silc_pkcs_unregister_all();
-      silc_hash_unregister_all();
-      silc_hmac_unregister_all();
-    }
+  if (client->rng)
+    silc_rng_free(client->rng);
 
-    silc_hash_free(client->md5hash);
-    silc_hash_free(client->sha1hash);
-    silc_hmac_free(client->internal->md5hmac);
-    silc_hmac_free(client->internal->sha1hmac);
-    silc_free(client->internal->params);
-    silc_free(client->internal->silc_client_version);
-    silc_free(client->internal);
-    silc_free(client);
+  if (!client->internal->params->dont_register_crypto_library) {
+    silc_cipher_unregister_all();
+    silc_pkcs_unregister_all();
+    silc_hash_unregister_all();
+    silc_hmac_unregister_all();
   }
+
+  silc_free(client->username);
+  silc_free(client->hostname);
+  silc_free(client->realname);
+  silc_free(client->internal->params);
+  silc_free(client->internal->silc_client_version);
+  silc_free(client->internal);
+  silc_free(client);
 }
 
 /* Initializes the client. This makes all the necessary steps to make
    the client ready to be run. One must call silc_client_run to run the
    client. Returns FALSE if error occured, TRUE otherwise. */
 
-SilcBool silc_client_init(SilcClient client)
+SilcBool silc_client_init(SilcClient client, const char *username,
+                         const char *hostname, const char *realname)
 {
   SILC_LOG_DEBUG(("Initializing client"));
 
-  assert(client);
-  assert(client->username);
-  assert(client->hostname);
-  assert(client->realname);
+  if (!client)
+    return FALSE;
+
+  if (!username || !hostname) {
+    SILC_LOG_ERROR(("Username, hostname and realname must be given to "
+                   "silc_client_init"));
+    return FALSE;
+  }
+  if (!realname)
+    realname = username;
 
   /* Validate essential strings */
-  if (client->nickname)
-    if (!silc_identifier_verify(client->nickname, strlen(client->nickname),
-                               SILC_STRING_UTF8, 128)) {
-      SILC_LOG_ERROR(("Malformed nickname '%s'", client->nickname));
-      return FALSE;
-    }
-  if (!silc_identifier_verify(client->username, strlen(client->username),
+  if (!silc_identifier_verify(username, strlen(username),
                              SILC_STRING_UTF8, 128)) {
-    SILC_LOG_ERROR(("Malformed username '%s'", client->username));
+    SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
+                   client->username));
     return FALSE;
   }
-  if (!silc_identifier_verify(client->hostname, strlen(client->hostname),
+  if (!silc_identifier_verify(hostname, strlen(hostname),
                              SILC_STRING_UTF8, 256)) {
-    SILC_LOG_ERROR(("Malformed hostname '%s'", client->hostname));
+    SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
+                   client->hostname));
     return FALSE;
   }
-  if (!silc_utf8_valid(client->realname, strlen(client->realname))) {
-    SILC_LOG_ERROR(("Malformed realname '%s'", client->realname));
+  if (!silc_utf8_valid(realname, strlen(realname))) {
+    SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
+                   client->realname));
     return FALSE;
   }
 
+  /* Take the name strings */
+  client->username = strdup(username);
+  client->hostname = strdup(hostname);
+  client->realname = strdup(realname);
+  if (!username || !hostname || !realname)
+    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
@@ -1087,19 +1085,15 @@ SilcBool silc_client_init(SilcClient client)
     silc_hmac_register_default();
   }
 
-  /* Initialize hash functions for client to use */
-  silc_hash_alloc("md5", &client->md5hash);
-  silc_hash_alloc("sha1", &client->sha1hash);
-
   /* Initialize random number generator */
   client->rng = silc_rng_alloc();
+  if (!client->rng)
+    return FALSE;
   silc_rng_init(client->rng);
   silc_rng_global_init(client->rng);
 
   /* Initialize the scheduler */
-  client->schedule =
-    silc_schedule_init(client->internal->params->task_max ?
-                      client->internal->params->task_max : 200, client);
+  client->schedule = silc_schedule_init(0, client);
   if (!client->schedule)
     return FALSE;
 
@@ -1110,18 +1104,21 @@ SilcBool silc_client_init(SilcClient client)
   if (!client->internal->packet_engine)
     return FALSE;
 
-  /* Initialize FSM */
-  if (!silc_fsm_init(&client->internal->fsm, client, NULL, NULL,
-                    client->schedule))
-    return FALSE;
-  silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
-
   /* Allocate client lock */
   silc_mutex_alloc(&client->internal->lock);
 
   /* Register commands */
   silc_client_commands_register(client);
 
+  /* Initialize and start the client FSM */
+  silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
+  silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
+  silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
+
+  /* Signal the application when we are running */
+  client->internal->run_callback = TRUE;
+  SILC_FSM_SEMA_POST(&client->internal->wait_event);
+
   return TRUE;
 }
 
@@ -1134,7 +1131,6 @@ void silc_client_stop(SilcClient client)
 
   silc_schedule_stop(client->schedule);
   silc_schedule_uninit(client->schedule);
-
   silc_client_commands_unregister(client);
 
   SILC_LOG_DEBUG(("Client stopped"));
@@ -1147,13 +1143,6 @@ void silc_client_run(SilcClient client)
 {
   SILC_LOG_DEBUG(("Starting SILC client"));
 
-  /* Start the client */
-  silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
-
-  /* Signal the application when we are running */
-  client->internal->run_callback = TRUE;
-  SILC_FSM_SEMA_POST(&client->internal->wait_event);
-
   /* Run the scheduler */
   silc_schedule(client->schedule);
 }
@@ -1163,5 +1152,5 @@ void silc_client_run(SilcClient client)
 
 void silc_client_run_one(SilcClient client)
 {
-  silc_schedule_one(client->schedule, -1);
+  silc_schedule_one(client->schedule, 0);
 }