#include "silcclient.h"
#include "client_internal.h"
-/************************** Types and definitions ***************************/
-
-
/************************ Static utility functions **************************/
/* Connection machine FSM destructor. This will finish the thread where
/* Delete connection */
silc_client_del_connection(conn->client, conn);
- /* Finish the thread were this machine was running */
+ /* Finish the thread were this machine was running. Its destructor is the
+ silc_client_connection_finished. */
silc_fsm_finish(thread);
}
void *stream_context)
{
SilcClientConnection conn = stream_context;
- SilcClient client = conn->client;
SILC_LOG_DEBUG(("Remote disconnected connection"));
- /* Call connection callback */
- if (!conn->internal->callback_called)
- conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, 0, NULL,
- conn->callback_context);
- conn->internal->callback_called = TRUE;
-
/* 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);
SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
+ /* 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);
}
}
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)
SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
/* Wait until this thread is terminated from the machine destructor */
- SILC_FSM_WAIT;
+ return SILC_FSM_WAIT;
}
/* Connection machine main state. This handles various connection related
/* 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,
NULL, NULL, FALSE);
silc_fsm_start_sync(thread, silc_client_st_connect);
- SILC_FSM_CONTINUE;
+ return SILC_FSM_CONTINUE;
}
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,
NULL, NULL, FALSE);
silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
- SILC_FSM_CONTINUE;
+ return SILC_FSM_CONTINUE;
}
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,
NULL, NULL, FALSE);
silc_fsm_start_sync(thread, silc_client_st_rekey);
- SILC_FSM_CONTINUE;
- }
-
- if (conn->internal->disconnected) {
- /** Event: disconnected */
- SILC_LOG_DEBUG(("Event: disconnected"));
- silc_fsm_next(fsm, silc_client_connection_st_close);
- SILC_FSM_YIELD;
+ return SILC_FSM_CONTINUE;
}
/* NOT REACHED */
SILC_ASSERT(FALSE);
- SILC_FSM_CONTINUE;
+ return SILC_FSM_CONTINUE;
}
/* Packet processor thread. Each incoming packet is processed in FSM
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:
SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
silc_packet_free(packet);
- SILC_FSM_FINISH;
+ return SILC_FSM_FINISH;
break;
default:
silc_packet_free(packet);
- SILC_FSM_FINISH;
+ return SILC_FSM_FINISH;
break;
}
- SILC_FSM_CONTINUE;
+ return SILC_FSM_CONTINUE;
}
/* 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 must be
- already called back to application before getting here. */
+ is deleted in the machine destructor. The connection callback is called
+ in this state if it is set. */
SILC_FSM_STATE(silc_client_connection_st_close)
{
}
/* Give threads time to finish */
- SILC_FSM_YIELD;
+ return SILC_FSM_YIELD;
}
/* Abort ongoing event */
if (silc_fsm_is_started(&conn->internal->event_thread)) {
SILC_LOG_DEBUG(("Finish event thread"));
silc_fsm_continue_sync(&conn->internal->event_thread);
- SILC_FSM_YIELD;
+ return SILC_FSM_YIELD;
}
+ /* Call the connection callback */
+ if (conn->callback)
+ conn->callback(conn->client, conn, conn->internal->status,
+ conn->internal->error, conn->internal->disconnect_message,
+ conn->callback_context);
+ silc_free(conn->internal->disconnect_message);
+
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"));
- SILC_FSM_FINISH;
+ return SILC_FSM_FINISH;
}
/* Received error packet from server. Send it to application. */
silc_free(msg);
silc_packet_free(packet);
- SILC_FSM_FINISH;
+ return SILC_FSM_FINISH;
}
/* Received disconnect packet from server. We close the connection and
SILC_FSM_STATE(silc_client_disconnect)
{
SilcClientConnection conn = fsm_context;
- SilcClient client = conn->client;
SilcPacket packet = state_context;
SilcStatus status;
char *message = NULL;
if (silc_buffer_len(&packet->buffer) < 1) {
silc_packet_free(packet);
- SILC_FSM_FINISH;
+ return SILC_FSM_FINISH;
}
status = (SilcStatus)packet->buffer.data[0];
silc_buffer_len(&packet->buffer));
/* Call connection callback */
- if (!conn->internal->callback_called)
- conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
- message, conn->callback_context);
- conn->internal->callback_called = TRUE;
-
- silc_free(message);
- silc_packet_free(packet);
+ conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
+ conn->internal->error = status;
+ conn->internal->disconnect_message = message;
/* Signal to close connection */
if (!conn->internal->disconnected) {
SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
}
- SILC_FSM_FINISH;
+ silc_packet_free(packet);
+
+ return SILC_FSM_FINISH;
}
/*************************** Main client machine ****************************/
/* Process events */
- if (client->internal->run_callback && client->internal->running) {
+ if (client->internal->run_callback) {
/* Call running callbcak back to application */
- SILC_LOG_DEBUG(("We are up, call running callback"));
client->internal->run_callback = FALSE;
- client->internal->running(client, client->internal->running_context);
- SILC_FSM_CONTINUE;
+ if (client->internal->running) {
+ SILC_LOG_DEBUG(("We are up, call running callback"));
+ client->internal->running(client, client->internal->running_context);
+ }
+ return SILC_FSM_CONTINUE;
}
if (client->internal->connection_closed) {
if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
client->internal->stop)
SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
- SILC_FSM_CONTINUE;
+ return SILC_FSM_CONTINUE;
}
if (client->internal->stop) {
/* Stop client libarry. If we have running connections, wait until
they finish first. */
- SILC_LOG_DEBUG(("Event: stop"));
- if (silc_atomic_get_int16(&client->internal->conns) == 0)
+ if (silc_atomic_get_int16(&client->internal->conns) == 0) {
+ SILC_LOG_DEBUG(("Event: stop"));
silc_fsm_next(fsm, silc_client_st_stop);
- SILC_FSM_CONTINUE;
+ }
+ return SILC_FSM_CONTINUE;
}
/* NOT REACHED */
SILC_ASSERT(FALSE);
- SILC_FSM_CONTINUE;
+ return SILC_FSM_CONTINUE;
}
/* Stop event. Stops the client library. */
if (client->internal->running)
client->internal->running(client, client->internal->running_context);
- SILC_FSM_FINISH;
+ return SILC_FSM_FINISH;
}
/******************************* Private API ********************************/
/* 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,
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);
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 */
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));
if (!client || !remote_host)
return NULL;
+ if (client->internal->run_callback) {
+ SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
+ "callback has not been called yet."));
+ 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) {
if (!client || !remote_host)
return NULL;
+ if (client->internal->run_callback) {
+ SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
+ "callback has not been called yet."));
+ 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) {
if (!client || !stream)
return NULL;
+ if (client->internal->run_callback) {
+ SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
+ "callback has not been called yet."));
+ return NULL;
+ }
+
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);
}
/* 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;
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);
void silc_client_free(SilcClient client)
{
- silc_schedule_uninit(client->schedule);
+ if (client->schedule)
+ silc_schedule_uninit(client->schedule);
if (client->rng)
silc_rng_free(client->rng);
silc_hmac_unregister_all();
}
+ if (client->internal->packet_engine)
+ silc_packet_engine_stop(client->internal->packet_engine);
+ if (client->internal->ftp_sessions)
+ silc_dlist_uninit(client->internal->ftp_sessions);
+ if (client->internal->lock)
+ silc_mutex_free(client->internal->lock);
silc_atomic_uninit16(&client->internal->conns);
silc_free(client->username);
silc_free(client->hostname);
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
{
SILC_LOG_DEBUG(("Stopping client"));
+ if (!silc_fsm_is_started(&client->internal->fsm)) {
+ SILC_LOG_WARNING(("Attempting to stop client library before it has been "
+ "started (silc_client_init not called)"));
+ return;
+ }
+
client->internal->running = (SilcClientRunning)stopped;
client->internal->running_context = context;