Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2006 Pekka Riikonen
+ Copyright (C) 1997 - 2007 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#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);
}
/* Signal client that we have finished */
silc_atomic_sub_int16(&client->internal->conns, 1);
client->internal->connection_closed = TRUE;
- SILC_FSM_SEMA_POST(&client->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
silc_fsm_free(fsm);
}
-
/* Packet FSM thread destructor */
static void silc_client_packet_destructor(SilcFSMThread thread,
case SILC_PACKET_KEY_EXCHANGE_2:
case SILC_PACKET_REKEY_DONE:
case SILC_PACKET_CONNECTION_AUTH:
- case SILC_PACKET_CONNECTION_AUTH_REQUEST:
return FALSE;
break;
}
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_SEMA_POST(&conn->internal->wait_event);
+ 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_SEMA_POST(&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);
}
}
connfsm = &conn->internal->fsm;
silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
fsm, conn->internal->schedule);
- silc_fsm_sema_init(&conn->internal->wait_event, connfsm, 0);
+ 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_SEMA_POST(&conn->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
if (conn->internal->key_exchange)
- SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
/* Wait until this thread is terminated from the machine destructor */
return SILC_FSM_WAIT;
SilcFSMThread thread;
/* Wait for events */
- SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
+ SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
/* 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,
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,
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,
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;
case SILC_PACKET_FTP:
/* File transfer packet */
- // silc_client_ftp(client, conn, packet);
+ silc_fsm_next(fsm, silc_client_ftp);
break;
case SILC_PACKET_CHANNEL_KEY:
break;
case SILC_PACKET_CONNECTION_AUTH_REQUEST:
- /* Reply to connection authentication request to resolve authentication
- method from server. */
- // silc_client_connection_auth_request(client, conn, packet);
+ /** Connection auth resolve reply */
+ silc_fsm_next(fsm, silc_client_connect_auth_request);
break;
case SILC_PACKET_REKEY:
/* Signal to start rekey */
conn->internal->rekey_responder = TRUE;
conn->internal->rekeying = TRUE;
- SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
silc_packet_free(packet);
return SILC_FSM_FINISH;
/* 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)
{
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"));
-
return SILC_FSM_FINISH;
}
SILC_FSM_STATE(silc_client_disconnect)
{
SilcClientConnection conn = fsm_context;
- SilcClient client = conn->client;
SilcPacket packet = state_context;
SilcStatus status;
char *message = NULL;
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) {
conn->internal->disconnected = TRUE;
- SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
}
+ silc_packet_free(packet);
+
return SILC_FSM_FINISH;
}
SilcClient client = fsm_context;
/* Wait for events */
- SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
+ SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
/* 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);
+ 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;
}
client->internal->connection_closed = FALSE;
if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
client->internal->stop)
- SILC_FSM_SEMA_POST(&client->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
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);
+ }
return SILC_FSM_CONTINUE;
}
/* 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_SEMA_POST(&conn->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
}
}
-#if 0
-/* Processes incoming connection authentication method request packet.
- It is a reply to our previously sent request. The packet can be used
- to resolve the authentication method for the current session if the
- client does not know it beforehand. */
-
-void silc_client_connection_auth_request(SilcClient client,
- SilcClientConnection conn,
- SilcPacketContext *packet)
-{
- SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- SilcUInt16 conn_type, auth_meth;
- int ret;
-
- /* If we haven't send our request then ignore this one. */
- if (!conn->internal->connauth)
- return;
-
- /* Parse the payload */
- ret = silc_buffer_unformat(packet->buffer,
- SILC_STR_UI_SHORT(&conn_type),
- SILC_STR_UI_SHORT(&auth_meth),
- SILC_STR_END);
- if (ret == -1)
- auth_meth = SILC_AUTH_NONE;
-
- /* Call the request callback to notify application for received
- authentication method information. */
- if (conn->internal->connauth->callback)
- (*conn->internal->connauth->callback)(client, conn, auth_meth,
- conn->internal->connauth->context);
-
- silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
-
- silc_free(conn->internal->connauth);
- conn->internal->connauth = NULL;
-}
-
-/* Timeout task callback called if the server does not reply to our
- connection authentication method request in the specified time interval. */
-
-SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
-{
- SilcClientConnection conn = (SilcClientConnection)context;
- SilcClient client = conn->client;
-
- if (!conn->internal->connauth)
- return;
-
- /* Call the request callback to notify application */
- if (conn->internal->connauth->callback)
- (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
- conn->internal->connauth->context);
-
- silc_free(conn->internal->connauth);
- conn->internal->connauth = NULL;
-}
-
-/* This function can be used to request the current authentication method
- from the server. This may be called when connecting to the server
- and the client library requests the authentication data from the
- application. If the application does not know the current authentication
- method it can request it from the server using this function.
- The `callback' with `context' will be called after the server has
- replied back with the current authentication method. */
-
-void
-silc_client_request_authentication_method(SilcClient client,
- SilcClientConnection conn,
- SilcConnectionAuthRequest callback,
- void *context)
-{
- SilcClientConnAuthRequest connauth;
- SilcBuffer packet;
-
- assert(client && conn);
- connauth = silc_calloc(1, sizeof(*connauth));
- connauth->callback = callback;
- connauth->context = context;
-
- if (conn->internal->connauth)
- silc_free(conn->internal->connauth);
-
- conn->internal->connauth = connauth;
-
- /* Assemble the request packet and send it to the server */
- packet = silc_buffer_alloc(4);
- silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
- silc_buffer_format(packet,
- SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
- SILC_STR_UI_SHORT(SILC_AUTH_NONE),
- SILC_STR_END);
- silc_client_packet_send(client, conn->sock,
- SILC_PACKET_CONNECTION_AUTH_REQUEST,
- NULL, 0, NULL, NULL,
- packet->data, packet->len, FALSE);
- silc_buffer_free(packet);
-
- /* Register a timeout in case server does not reply anything back. */
- connauth->timeout =
- silc_schedule_task_add(client->schedule, conn->sock->sock,
- silc_client_request_authentication_method_timeout,
- conn,
- client->internal->params->connauth_request_secs, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-}
-#endif /* 0 */
-
/* Allocates new client object. This has to be done before client may
work. After calling this one must call silc_client_init to initialize
the client. The `application' is application specific user data pointer
if (params)
memcpy(new_client->internal->params, params, sizeof(*params));
- if (!new_client->internal->params->connauth_request_secs)
- new_client->internal->params->connauth_request_secs = 2;
-
new_client->internal->params->
nickname_format[sizeof(new_client->internal->
params->nickname_format) - 1] = 0;
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
client->internal->running = running;
client->internal->running_context = context;
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_event_init(&client->internal->wait_event, &client->internal->fsm);
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);
+ SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
return TRUE;
}
{
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;
/* Signal to stop */
client->internal->stop = TRUE;
- SILC_FSM_SEMA_POST(&client->internal->wait_event);
+ SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
}