X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient.c;h=449fc375ce152b33bca4e0c3bbd06ce53870766d;hp=5fdb9dad3ef5b6f6c54131fdcf310dfb5edba441;hb=HEAD;hpb=e61ed2bf6601c99fbf370b909e8ec746268cb4c5 diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 5fdb9dad..449fc375 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2006 Pekka Riikonen + Copyright (C) 1997 - 2008 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 @@ -16,15 +16,11 @@ GNU General Public License for more details. */ -/* $Id$ */ #include "silc.h" #include "silcclient.h" #include "client_internal.h" -/************************** Types and definitions ***************************/ - - /************************ Static utility functions **************************/ /* Connection machine FSM destructor. This will finish the thread where @@ -37,13 +33,34 @@ static void silc_client_connection_destructor(SilcFSM fsm, SilcClientConnection conn = fsm_context; SilcFSMThread thread = destructor_context; + SILC_LOG_DEBUG(("Connection %p finished", conn)); + /* 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); } +/* Connection thread FSM destructor. This was the thread where the connection + machine was running (may be real thread). From here we notify client + that the connection thread has finished. */ + +static void silc_client_connection_finished(SilcFSMThread fsm, + void *fsm_context, + void *destructor_context) +{ + SilcClient client = silc_fsm_get_state_context(fsm); + + /* Signal client that we have finished */ + silc_atomic_sub_int32(&client->internal->conns, 1); + client->internal->connection_closed = TRUE; + SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event); + + silc_fsm_free(fsm); +} + /* Packet FSM thread destructor */ static void silc_client_packet_destructor(SilcFSMThread thread, @@ -78,10 +95,8 @@ static SilcBool silc_client_packet_receive(SilcPacketEngine engine, case SILC_PACKET_KEY_EXCHANGE: case SILC_PACKET_KEY_EXCHANGE_1: case SILC_PACKET_KEY_EXCHANGE_2: - case SILC_PACKET_REKEY: case SILC_PACKET_REKEY_DONE: case SILC_PACKET_CONNECTION_AUTH: - case SILC_PACKET_CONNECTION_AUTH_REQUEST: return FALSE; break; } @@ -113,7 +128,16 @@ static void silc_client_packet_eos(SilcPacketEngine engine, void *callback_context, void *stream_context) { - SILC_LOG_DEBUG(("End of stream received")); + SilcClientConnection conn = stream_context; + + SILC_LOG_DEBUG(("Remote disconnected connection")); + + /* 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); + } } /* Packet engine callback to indicate error */ @@ -124,7 +148,15 @@ static void silc_client_packet_error(SilcPacketEngine engine, void *callback_context, void *stream_context) { + SilcClient client = callback_context; + SilcClientConnection conn = stream_context; + + /* Read and write errors are silent */ + if (error == SILC_PACKET_ERR_READ || error == SILC_PACKET_ERR_WRITE) + return; + client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, + (char *)silc_packet_error_string(error)); } /* Packet stream callbacks */ @@ -143,6 +175,28 @@ void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context, silc_fsm_free(fsm); } +/* Connect abort operation */ + +static void silc_client_connect_abort(SilcAsyncOperation op, void *context) +{ + SilcClientConnection conn = context; + + 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; + + /* 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); + } +} /************************** Connection's machine ****************************/ @@ -161,14 +215,16 @@ SILC_FSM_STATE(silc_client_connection_st_start) 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; @@ -184,14 +240,22 @@ SILC_FSM_STATE(silc_client_connection_st_run) 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, @@ -203,6 +267,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, @@ -211,11 +276,15 @@ SILC_FSM_STATE(silc_client_connection_st_run) return SILC_FSM_CONTINUE; } - if (conn->internal->disconnected) { - /** Event: disconnected */ - SILC_LOG_DEBUG(("Event: disconnected")); - conn->internal->disconnected = FALSE; - silc_fsm_next(fsm, silc_client_connection_st_close); + 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); return SILC_FSM_CONTINUE; } @@ -229,6 +298,7 @@ SILC_FSM_STATE(silc_client_connection_st_run) SILC_FSM_STATE(silc_client_connection_st_packet) { + SilcClientConnection conn = fsm_context; SilcPacket packet = state_context; SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type))); @@ -247,7 +317,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet) 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: @@ -281,8 +351,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,11 +363,21 @@ 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 - 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_EVENT_SIGNAL(&conn->internal->wait_event); + + silc_packet_free(packet); + return SILC_FSM_FINISH; break; default: @@ -309,24 +389,61 @@ SILC_FSM_STATE(silc_client_connection_st_packet) return SILC_FSM_CONTINUE; } -/* Disconnection even to close remote connection. We close the connection +/* 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) { SilcClientConnection conn = fsm_context; + SilcClientCommandContext cmd; + + /* Finish running command threads. This will also finish waiting packet + thread, as they are always waiting for some command. If any thread is + waiting something else than command, they must be finished explicitly. */ + if (silc_list_count(conn->internal->pending_commands)) { + SILC_LOG_DEBUG(("Finish pending commands")); + silc_list_start(conn->internal->pending_commands); + while ((cmd = silc_list_get(conn->internal->pending_commands))) { + if (silc_fsm_is_started(&cmd->thread)) { + cmd->verbose = FALSE; + silc_fsm_continue_sync(&cmd->thread); + } + } - SILC_LOG_DEBUG(("Closing remote connection")); + /* Give threads time to finish */ + return SILC_FSM_YIELD; + } - /* XXX abort any ongoing events (protocols) */ + /* Abort ongoing event */ + if (conn->internal->op) { + SILC_LOG_DEBUG(("Abort event")); + silc_async_abort(conn->internal->op, NULL, NULL); + conn->internal->op = NULL; + } - /* Close connection */ - silc_packet_stream_destroy(conn->stream); + /* If event thread is running, finish it. */ + if (silc_fsm_is_started(&conn->internal->event_thread)) { + SILC_LOG_DEBUG(("Finish event thread")); + silc_fsm_continue_sync(&conn->internal->event_thread); + return SILC_FSM_YIELD; + } - SILC_LOG_DEBUG(("Finishing connection machine")); + /* 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. */ + if (conn->stream) + silc_packet_stream_destroy(conn->stream); + SILC_LOG_DEBUG(("Finishing connection machine")); return SILC_FSM_FINISH; } @@ -356,7 +473,6 @@ SILC_FSM_STATE(silc_client_error) SILC_FSM_STATE(silc_client_disconnect) { SilcClientConnection conn = fsm_context; - SilcClient client = conn->client; SilcPacket packet = state_context; SilcStatus status; char *message = NULL; @@ -378,15 +494,17 @@ SILC_FSM_STATE(silc_client_disconnect) silc_buffer_len(&packet->buffer)); /* Call connection callback */ - conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status, - message, conn->callback_context); - - 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 */ - conn->internal->disconnected = TRUE; - SILC_FSM_SEMA_POST(&conn->internal->wait_event); + if (!conn->internal->disconnected) { + conn->internal->disconnected = TRUE; + SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event); + } + + silc_packet_free(packet); return SILC_FSM_FINISH; } @@ -400,14 +518,37 @@ SILC_FSM_STATE(silc_client_st_run) 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->ops->running) { + if (client->internal->run_callback) { /* Call running callbcak back to application */ client->internal->run_callback = FALSE; - client->internal->ops->running(client, client->application); + 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) { + /* A connection finished */ + SILC_LOG_DEBUG(("Event: connection closed")); + client->internal->connection_closed = FALSE; + if (silc_atomic_get_int32(&client->internal->conns) == 0 && + client->internal->stop) + 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. */ + if (silc_atomic_get_int32(&client->internal->conns) == 0) { + SILC_LOG_DEBUG(("Event: stop")); + silc_fsm_next(fsm, silc_client_st_stop); + } return SILC_FSM_CONTINUE; } @@ -416,13 +557,33 @@ SILC_FSM_STATE(silc_client_st_run) return SILC_FSM_CONTINUE; } +/* Stop event. Stops the client library. */ + +SILC_FSM_STATE(silc_client_st_stop) +{ + SilcClient client = fsm_context; + + SILC_LOG_DEBUG(("Client stopped")); + + /* Stop scheduler */ + silc_schedule_stop(client->schedule); + silc_client_commands_unregister(client); + + /* Call stopped callback to application */ + if (client->internal->running) + client->internal->running(client, client->internal->running_context); + + 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, @@ -456,139 +617,145 @@ silc_client_add_connection(SilcClient client, 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; } + + /* Set parameters */ if (params) conn->internal->params = *params; + if (!conn->internal->params.rekey_secs) + conn->internal->params.rekey_secs = 3600; +#ifndef SILC_DIST_INPLACE + if (conn->internal->params.rekey_secs < 300) + conn->internal->params.rekey_secs = 300; +#endif /* SILC_DIST_INPLACE */ + conn->internal->verbose = TRUE; silc_list_init(conn->internal->pending_commands, struct SilcClientCommandContextStruct, next); silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next); - 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; + /* Allocate client, channel and serve caches */ + 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(); + 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_fsm_destructor, NULL, + silc_client_connection_finished, NULL, client->internal->params->threads); if (!thread) { silc_client_del_connection(client, conn); return NULL; } + silc_fsm_set_state_context(thread, client); silc_fsm_start(thread, silc_client_connection_st_start); + SILC_LOG_DEBUG(("New connection %p", conn)); + silc_atomic_add_int32(&client->internal->conns, 1); + 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; + SilcFSMThread thread; - /* 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); - } + SILC_LOG_DEBUG(("Freeing connection %p", conn)); - 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); - } + silc_schedule_task_del_by_context(conn->internal->schedule, conn); + /* Free all cache entries */ + if (conn->internal->server_cache) { if (silc_idcache_get_all(conn->internal->server_cache, &list)) { - ret = silc_idcache_list_first(list, &entry); - while (ret) { + silc_list_start(list); + while ((entry = silc_list_get(list))) 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; + } + 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); + } } - - 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); + } + 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); } - - silc_free(conn->internal); - memset(conn, 0, sizeof(*conn)); - silc_free(conn); - - silc_dlist_del(client->internal->conns, conn); } -#endif /* 0 */ -} + /* 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); + + 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->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)); + silc_free(conn); +} /******************************* Client API *********************************/ @@ -596,26 +763,35 @@ void silc_client_del_connection(SilcClient client, SilcClientConnection conn) to remote SILC server. Performs key exchange also. Returns the connection context to the connection callback. */ -SilcBool silc_client_connect_to_server(SilcClient client, - SilcClientConnectionParams *params, - SilcPublicKey public_key, - SilcPrivateKey private_key, - char *remote_host, int port, - SilcClientConnectCallback callback, - void *context) +SilcAsyncOperation +silc_client_connect_to_server(SilcClient client, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + char *remote_host, int port, + SilcClientConnectCallback callback, + void *context) { SilcClientConnection conn; + SILC_LOG_DEBUG(("Connecting to server")); + if (!client || !remote_host) - return FALSE; + 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) { callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context); - return FALSE; + return NULL; } client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, @@ -624,78 +800,98 @@ SilcBool silc_client_connect_to_server(SilcClient client, /* Signal connection machine to start connecting */ conn->internal->connect = TRUE; - return TRUE; + return conn->internal->cop; } /* Connects to remote client. Performs key exchange also. Returns the connection context to the connection callback. */ -SilcBool silc_client_connect_to_client(SilcClient client, - SilcClientConnectionParams *params, - SilcPublicKey public_key, - SilcPrivateKey private_key, - char *remote_host, int port, - SilcClientConnectCallback callback, - void *context) +SilcAsyncOperation +silc_client_connect_to_client(SilcClient client, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + char *remote_host, int port, + SilcClientConnectCallback callback, + void *context) { SilcClientConnection conn; + SILC_LOG_DEBUG(("Connecting to client")); + if (!client || !remote_host) - return FALSE; + 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) { callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context); - return FALSE; + return NULL; } - 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; + return conn->internal->cop; } /* Starts key exchange in the remote stream indicated by `stream'. This creates the connection context and returns it in the connection callback. */ -SilcBool silc_client_key_exchange(SilcClient client, - SilcClientConnectionParams *params, - SilcPublicKey public_key, - SilcPrivateKey private_key, - SilcStream stream, - SilcConnectionType conn_type, - SilcClientConnectCallback callback, - void *context) +SilcAsyncOperation +silc_client_key_exchange(SilcClient client, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + SilcStream stream, + SilcConnectionType conn_type, + SilcClientConnectCallback callback, + void *context) { SilcClientConnection conn; const char *host; SilcUInt16 port; + SILC_LOG_DEBUG(("Performing key exchange")); + if (!client || !stream) - return FALSE; + return NULL; - if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) - return FALSE; + 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); + return NULL; + } /* 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 FALSE; + return NULL; } - conn->stream = (void *)stream; + conn->internal->user_stream = stream; /* Signal connection to start key exchange */ conn->internal->key_exchange = TRUE; - return TRUE; + return conn->internal->cop; } /* Closes remote connection */ @@ -703,280 +899,16 @@ SilcBool silc_client_key_exchange(SilcClient client, void silc_client_close_connection(SilcClient client, SilcClientConnection conn) { + SILC_LOG_DEBUG(("Closing connection %p", conn)); -} - -#if 0 -/* Finalizes the connection to the remote SILC server. This is called - after authentication protocol has been completed. This send our - user information to the server to receive our client ID from - server. */ - -SILC_TASK_CALLBACK(silc_client_connect_to_server_final) -{ - SilcProtocol protocol = (SilcProtocol)context; - SilcClientConnAuthInternalContext *ctx = - (SilcClientConnAuthInternalContext *)protocol->context; - SilcClient client = (SilcClient)ctx->client; - SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data; - SilcBuffer packet; - - SILC_LOG_DEBUG(("Start")); - - if (protocol->state == SILC_PROTOCOL_STATE_ERROR || - protocol->state == SILC_PROTOCOL_STATE_FAILURE) { - /* Error occured during protocol */ - SILC_LOG_DEBUG(("Error during authentication protocol")); - ctx->status = SILC_CLIENT_CONN_ERROR_AUTH; - goto err; - } - - if (conn->internal->params.detach_data) { - /* Send RESUME_CLIENT packet to the server, which is used to resume - old detached session back. */ - SilcBuffer auth; - SilcClientID *old_client_id; - unsigned char *old_id; - SilcUInt16 old_id_len; - - if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) { - ctx->status = SILC_CLIENT_CONN_ERROR_RESUME; - goto err; - } - - old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT); - if (!old_client_id) { - silc_free(old_id); - ctx->status = SILC_CLIENT_CONN_ERROR_RESUME; - goto err; - } - - /* Generate authentication data that server will verify */ - auth = silc_auth_public_key_auth_generate(client->public_key, - client->private_key, - client->rng, - conn->internal->hash, - old_client_id, SILC_ID_CLIENT); - if (!auth) { - silc_free(old_client_id); - silc_free(old_id); - ctx->status = SILC_CLIENT_CONN_ERROR_RESUME; - goto err; - } - - packet = silc_buffer_alloc_size(2 + old_id_len + auth->len); - silc_buffer_format(packet, - SILC_STR_UI_SHORT(old_id_len), - SILC_STR_UI_XNSTRING(old_id, old_id_len), - SILC_STR_UI_XNSTRING(auth->data, auth->len), - SILC_STR_END); - - /* Send the packet */ - silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT, - NULL, 0, NULL, NULL, - packet->data, packet->len, TRUE); - silc_buffer_free(packet); - silc_buffer_free(auth); - silc_free(old_client_id); - silc_free(old_id); - } else { - /* Send NEW_CLIENT packet to the server. We will become registered - to the SILC network after sending this packet and we will receive - client ID from the server. */ - packet = silc_buffer_alloc(2 + 2 + strlen(client->username) + - strlen(client->realname)); - silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); - silc_buffer_format(packet, - SILC_STR_UI_SHORT(strlen(client->username)), - SILC_STR_UI_XNSTRING(client->username, - strlen(client->username)), - SILC_STR_UI_SHORT(strlen(client->realname)), - SILC_STR_UI_XNSTRING(client->realname, - strlen(client->realname)), - SILC_STR_END); - - /* Send the packet */ - silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT, - NULL, 0, NULL, NULL, - packet->data, packet->len, TRUE); - silc_buffer_free(packet); - } - - /* Save remote ID. */ - conn->remote_id = ctx->dest_id; - conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER); - conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER); - - /* Register re-key timeout */ - conn->internal->rekey->timeout = client->internal->params->rekey_secs; - conn->internal->rekey->context = (void *)client; - silc_schedule_task_add(client->schedule, conn->sock->sock, - silc_client_rekey_callback, - (void *)conn->sock, conn->internal->rekey->timeout, 0, - SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - - silc_protocol_free(protocol); - silc_free(ctx->auth_data); - if (ctx->ske) - silc_ske_free(ctx->ske); - silc_socket_free(ctx->sock); - silc_free(ctx); - conn->sock->protocol = NULL; - return; - - err: - silc_protocol_free(protocol); - silc_free(ctx->auth_data); - silc_free(ctx->dest_id); - if (ctx->ske) - silc_ske_free(ctx->ske); - conn->sock->protocol = NULL; - silc_socket_free(ctx->sock); - - /* Notify application of failure */ - silc_schedule_task_add(client->schedule, ctx->sock->sock, - silc_client_connect_failure_auth, ctx, - 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); -} - -/* Client session resuming callback. If the session was resumed - this callback is called after the resuming is completed. This - will call the `connect' client operation to the application - since it has not been called yet. */ - -static void silc_client_resume_session_cb(SilcClient client, - SilcClientConnection conn, - SilcBool success, - void *context) -{ - SilcBuffer sidp; - - /* Notify application that connection is created to server */ - client->internal->ops->connected(client, conn, success ? - SILC_CLIENT_CONN_SUCCESS_RESUME : - SILC_CLIENT_CONN_ERROR_RESUME); - - if (success) { - /* Issue INFO command to fetch the real server name and server - information and other stuff. */ - silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL, - silc_client_command_reply_info_i, 0, - ++conn->cmd_ident); - sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); - silc_client_command_send(client, conn, SILC_COMMAND_INFO, - conn->cmd_ident, 1, 2, sidp->data, sidp->len); - silc_buffer_free(sidp); + /* 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); } } -/* 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 @@ -1009,19 +941,12 @@ 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; - - 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; + silc_atomic_init32(&new_client->internal->conns, 0); + return new_client; } @@ -1029,22 +954,29 @@ 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(); - } - - silc_free(client->internal->params); - silc_free(client->internal->silc_client_version); - silc_free(client->internal); - silc_free(client); - } + if (client->schedule) + silc_schedule_uninit(client->schedule); + + if (client->rng) + silc_rng_free(client->rng); + + if (!client->internal->params->dont_register_crypto_library) + silc_crypto_uninit(); + + 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_uninit32(&client->internal->conns); + 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 @@ -1052,18 +984,21 @@ void silc_client_free(SilcClient client) client. Returns FALSE if error occured, TRUE otherwise. */ SilcBool silc_client_init(SilcClient client, const char *username, - const char *hostname, const char *realname) + const char *hostname, const char *realname, + SilcClientRunning running, void *context) { SILC_LOG_DEBUG(("Initializing client")); if (!client) return FALSE; - if (!username || !hostname || !realname) { - SILC_LOG_ERROR(("Username, hostname and realname must be given to " + if (!username || !hostname) { + SILC_LOG_ERROR(("Username and hostname must be given to " "silc_client_init")); return FALSE; } + if (!realname) + realname = username; /* Validate essential strings */ if (!silc_identifier_verify(username, strlen(username), @@ -1091,28 +1026,33 @@ SilcBool silc_client_init(SilcClient client, const char *username, if (!username || !hostname || !realname) return FALSE; - if (!client->internal->params->dont_register_crypto_library) { + 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 - application might have registered earlier. */ - silc_cipher_register_default(); - silc_pkcs_register_default(); - silc_hash_register_default(); - silc_hmac_register_default(); - } + this has no effect. */ + silc_crypto_init(NULL); /* 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 : 0, client); + client->schedule = silc_schedule_init(0, client, NULL, NULL); if (!client->schedule) return FALSE; + /* Allocate client lock */ + silc_mutex_alloc(&client->internal->lock); + + /* Register commands */ + silc_client_commands_register(client); + /* Start packet engine */ client->internal->packet_engine = silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs, @@ -1120,42 +1060,20 @@ SilcBool silc_client_init(SilcClient client, const char *username, 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); - - /* Start the client machine */ + /* Initialize and start the client FSM */ + client->internal->running = running; + client->internal->running_context = context; + silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule); + 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; } -/* Stops the client. This is called to stop the client and thus to stop - the program. */ - -void silc_client_stop(SilcClient client) -{ - SILC_LOG_DEBUG(("Stopping client")); - - silc_schedule_stop(client->schedule); - silc_schedule_uninit(client->schedule); - silc_client_commands_unregister(client); - - SILC_LOG_DEBUG(("Client stopped")); -} - /* Starts the SILC client FSM machine and blocks here. When this returns the client has ended. */ @@ -1167,10 +1085,32 @@ void silc_client_run(SilcClient client) silc_schedule(client->schedule); } -/* Call scheduler one iteration and return. This cannot be called if threads - are in use. */ +/* Call scheduler one iteration and return. */ void silc_client_run_one(SilcClient client) { - silc_schedule_one(client->schedule, 0); + if (silc_fsm_is_started(&client->internal->fsm)) + silc_schedule_one(client->schedule, 0); +} + +/* Stops the client. This is called to stop the client and thus to stop + the program. */ + +void silc_client_stop(SilcClient client, SilcClientStopped stopped, + void *context) +{ + 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_EVENT_SIGNAL(&client->internal->wait_event); }