X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcclient%2Fclient.c;h=8834c272f92278f5a263594520cc3e9ac9e6d89a;hb=3df964f52d094db8ea9e8981fee73ff2a59e365c;hp=e4191d37ee35d1cd48d508ce36f4091f9c5b5492;hpb=087575e2f4248e52d8db5900f757b847e65d9d91;p=silc.git diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index e4191d37..8834c272 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 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 @@ -22,211 +22,357 @@ #include "silcclient.h" #include "client_internal.h" -/* Static task callback prototypes */ -SILC_TASK_CALLBACK(silc_client_connect_to_server_start); -SILC_TASK_CALLBACK(silc_client_connect_to_server_second); -SILC_TASK_CALLBACK(silc_client_connect_to_server_final); -SILC_TASK_CALLBACK(silc_client_rekey_final); +/************************** Types and definitions ***************************/ -static SilcBool silc_client_packet_parse(SilcPacketParserContext *parser_context, - void *context); -static void silc_client_packet_parse_type(SilcClient client, - SilcSocketConnection sock, - SilcPacketContext *packet); -void silc_client_resolve_auth_method(SilcBool success, - SilcProtocolAuthMeth auth_meth, - const unsigned char *auth_data, - SilcUInt32 auth_data_len, void *context); +SILC_FSM_STATE(silc_client_connection_st_run); +SILC_FSM_STATE(silc_client_new_id); -/* 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 - and caller must free it. */ -SilcClient silc_client_alloc(SilcClientOperations *ops, - SilcClientParams *params, - void *application, - const char *version_string) +/************************ Static utility functions **************************/ + +/* Packet engine callback to receive a packet */ + +static SilcBool silc_client_packet_receive(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacket packet, + void *callback_context, + void *stream_context) { - SilcClient new_client; + SilcClient client = callback_context; + SilcClientConnection conn = stream_context; - new_client = silc_calloc(1, sizeof(*new_client)); - new_client->application = application; + /* Packets we do not handle */ + switch (packet->type) { + case SILC_PACKET_HEARTBEAT: + case SILC_PACKET_SUCCESS: + case SILC_PACKET_FAILURE: + case SILC_PACKET_REJECT: + 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; + } - new_client->internal = silc_calloc(1, sizeof(*new_client->internal)); - new_client->internal->ops = ops; - new_client->internal->params = - silc_calloc(1, sizeof(*new_client->internal->params)); - if (!version_string) - version_string = silc_version_string; - new_client->internal->silc_client_version = strdup(version_string); + /* Signal packet processor thread for a new packet */ + conn->internal->new_packet = TRUE; + silc_fsm_set_state_context(&conn->internal->packet_thread, packet); + silc_fsm_continue_sync(&conn->internal->packet_thread); - if (params) - memcpy(new_client->internal->params, params, sizeof(*params)); + return TRUE; +} - if (!new_client->internal->params->task_max) - new_client->internal->params->task_max = 200; +/* Packet engine callback to indicate end of stream */ - if (!new_client->internal->params->rekey_secs) - new_client->internal->params->rekey_secs = 3600; +static void silc_client_packet_eos(SilcPacketEngine engine, + SilcPacketStream stream, + void *callback_context, + void *stream_context) +{ + SILC_LOG_DEBUG(("End of stream received")); +} - if (!new_client->internal->params->connauth_request_secs) - new_client->internal->params->connauth_request_secs = 2; +/* Packet engine callback to indicate error */ - new_client->internal->params-> - nickname_format[sizeof(new_client->internal-> - params->nickname_format) - 1] = 0; +static void silc_client_packet_error(SilcPacketEngine engine, + SilcPacketStream stream, + SilcPacketError error, + void *callback_context, + void *stream_context) +{ - return new_client; } -/* Frees client object and its internals. */ +/* Packet stream callbacks */ +static SilcPacketCallbacks silc_client_stream_cbs = +{ + silc_client_packet_receive, + silc_client_packet_eos, + silc_client_packet_error +}; -void silc_client_free(SilcClient client) +/* FSM destructor */ + +void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context, + void *destructor_context) { - if (client) { - if (client->rng) - silc_rng_free(client->rng); + silc_fsm_free(fsm); +} - 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_hash_free(client->md5hash); - silc_hash_free(client->sha1hash); - silc_hmac_free(client->internal->md5hmac); - silc_hmac_free(client->internal->sha1hmac); - silc_cipher_free(client->internal->none_cipher); - silc_free(client->internal->params); - silc_free(client->internal->silc_client_version); - silc_free(client->internal); - silc_free(client); - } +/************************** Connection's machine ****************************/ + +/* Start the connection's state machine. If threads are in use the machine + is always executed in a real thread. */ + +SILC_FSM_STATE(silc_client_connection_st_start) +{ + SilcClientConnection conn = fsm_context; + + /* Take scheduler for connection */ + conn->internal->schedule = silc_fsm_get_schedule(fsm); + + /*** Run connection machine */ + silc_fsm_init(&conn->internal->fsm, conn, NULL, NULL, + conn->internal->schedule); + silc_fsm_sema_init(&conn->internal->wait_event, &conn->internal->fsm, 0); + silc_fsm_start_sync(&conn->internal->fsm, silc_client_connection_st_run); + + /*** Run packet processor FSM thread */ + silc_fsm_thread_init(&conn->internal->packet_thread, &conn->internal->fsm, + conn, silc_client_fsm_destructor, NULL, FALSE); + silc_fsm_start_sync(&conn->internal->packet_thread, + silc_client_connection_st_packet); + + /* Schedule any events set in initialization */ + if (conn->internal->connect) + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + if (conn->internal->key_exchange) + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + + /* Wait until this thread is terminated */ + return SILC_FSM_WAIT; } -/* 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. */ +/* Connection machine main state. */ -SilcBool silc_client_init(SilcClient client) +SILC_FSM_STATE(silc_client_connection_st_run) { - SILC_LOG_DEBUG(("Initializing client")); + SilcClientConnection conn = fsm_context; - assert(client); - assert(client->username); - assert(client->hostname); - assert(client->realname); + /* Wait for events */ + SILC_FSM_SEMA_WAIT(&conn->internal->wait_event); - /* 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), - SILC_STRING_UTF8, 128)) { - SILC_LOG_ERROR(("Malformed username '%s'", client->username)); - return FALSE; + /* Process events */ + + if (conn->internal->connect) { + SILC_LOG_DEBUG(("Event: connect")); + conn->internal->connect = FALSE; + + /** Connect remote host */ + silc_fsm_thread_init(&conn->internal->event_thread, &conn->internal->fsm, + conn, NULL, NULL, FALSE); + silc_fsm_start_sync(&conn->internal->event_thread, silc_client_st_connect); + return SILC_FSM_CONTINUE; } - if (!silc_identifier_verify(client->hostname, strlen(client->hostname), - SILC_STRING_UTF8, 256)) { - SILC_LOG_ERROR(("Malformed hostname '%s'", client->hostname)); - return FALSE; + + if (conn->internal->key_exchange) { + SILC_LOG_DEBUG(("Event: key exchange")); + conn->internal->key_exchange = FALSE; + + /** Start key exchange */ + silc_fsm_thread_init(&conn->internal->event_thread, &conn->internal->fsm, + conn, NULL, NULL, FALSE); + silc_fsm_start_sync(&conn->internal->event_thread, + silc_client_st_connect_key_exchange); + return SILC_FSM_CONTINUE; } - if (!silc_utf8_valid(client->realname, strlen(client->realname))) { - SILC_LOG_ERROR(("Malformed realname '%s'", client->realname)); - return FALSE; + + if (conn->internal->disconnected) { + SILC_LOG_DEBUG(("Event: disconnected")); + conn->internal->disconnected = FALSE; + + return SILC_FSM_CONTINUE; } - 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(); + /* NOT REACHED */ +#if defined(SILC_DEBUG) + assert(FALSE); +#endif /* SILC_DEBUG */ + return SILC_FSM_CONTINUE; +} + +/* Connection's packet processor main state. Packet processor thread waits + here for a new packet and processes received packets. */ + +SILC_FSM_STATE(silc_client_connection_st_packet) +{ + SilcClientConnection conn = fsm_context; + SilcClient client = conn->client; + SilcPacket packet = state_context; + + /* Wait for packet to arrive */ + if (!conn->internal->new_packet) { + SILC_LOG_DEBUG(("Wait for packet")); + return SILC_FSM_WAIT; } + conn->internal->new_packet = FALSE; - /* Initialize hash functions for client to use */ - silc_hash_alloc("md5", &client->md5hash); - silc_hash_alloc("sha1", &client->sha1hash); + SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type))); - /* Initialize none cipher */ - silc_cipher_alloc("none", &client->internal->none_cipher); + switch (packet->type) { - /* Initialize random number generator */ - client->rng = silc_rng_alloc(); - silc_rng_init(client->rng); - silc_rng_global_init(client->rng); + case SILC_PACKET_PRIVATE_MESSAGE: + /** Private message */ + silc_fsm_next(fsm, silc_client_private_message); + break; - /* Register protocols */ - silc_client_protocols_register(); + case SILC_PACKET_CHANNEL_MESSAGE: + /* Channel message */ + // silc_client_channel_message(client, conn, packet); + break; - /* Initialize the scheduler */ - client->schedule = - silc_schedule_init(client->internal->params->task_max ? - client->internal->params->task_max : 200, client); - if (!client->schedule) - return FALSE; + case SILC_PACKET_FTP: + /* File transfer packet */ + // silc_client_ftp(client, conn, packet); + break; - /* Register commands */ - silc_client_commands_register(client); + case SILC_PACKET_CHANNEL_KEY: + /* Received channel key */ + // silc_client_channel_key(client, conn, packet); + break; - return TRUE; -} + case SILC_PACKET_COMMAND_REPLY: + /** Command reply */ + silc_fsm_next(fsm, silc_client_command_reply); + break; -/* Stops the client. This is called to stop the client and thus to stop - the program. */ + case SILC_PACKET_NOTIFY: + /* Notify */ + // silc_client_notify(client, conn, packet); + break; -void silc_client_stop(SilcClient client) -{ - SILC_LOG_DEBUG(("Stopping client")); + case SILC_PACKET_PRIVATE_MESSAGE_KEY: + /* Private message key indicator */ + // silc_client_private_message_key(client, conn, packet); + break; - silc_schedule_stop(client->schedule); - silc_schedule_uninit(client->schedule); + case SILC_PACKET_DISCONNECT: + /* Server disconnects */ + // silc_client_disconnect(client, conn, packet); + break; - silc_client_protocols_unregister(); - silc_client_commands_unregister(client); + case SILC_PACKET_ERROR: + /* Error by server */ + // silc_client_error(client, conn, packet); + break; - SILC_LOG_DEBUG(("Client stopped")); + case SILC_PACKET_KEY_AGREEMENT: + /* Key agreement */ + // silc_client_key_agreement(client, conn, packet); + break; + + case SILC_PACKET_COMMAND: + /** Command packet */ + silc_fsm_next(fsm, silc_client_command); + break; + + case SILC_PACKET_NEW_ID: + /** New ID */ + silc_fsm_next(fsm, silc_client_new_id); + + 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); + break; + + default: + silc_packet_free(packet); + break; + } + + return SILC_FSM_CONTINUE; } -/* Runs the client. This starts the scheduler from the utility library. - When this functions returns the execution of the appliation is over. */ -void silc_client_run(SilcClient client) +/*************************** Main client machine ****************************/ + +/* The client's main state where we wait for various events */ + +SILC_FSM_STATE(silc_client_st_run) { - SILC_LOG_DEBUG(("Running client")); + SilcClient client = fsm_context; - assert(client); - assert(client->pkcs); - assert(client->public_key); - assert(client->private_key); + /* Wait for events */ + SILC_FSM_SEMA_WAIT(&client->internal->wait_event); - /* Start the scheduler, the heart of the SILC client. When this returns - the program will be terminated. */ - silc_schedule(client->schedule); + /* Process events */ + + if (client->internal->run_callback && client->internal->ops->running) { + /* Call running callbcak back to application */ + client->internal->run_callback = FALSE; + client->internal->ops->running(client, client->application); + return SILC_FSM_CONTINUE; + } + + /* NOT REACHED */ +#if defined(SILC_DEBUG) + assert(FALSE); +#endif /* SILC_DEBUG */ + return SILC_FSM_CONTINUE; } -/* Runs the client and returns immeadiately. This function is used when - the SILC Client object indicated by the `client' is run under some - other scheduler, or event loop or main loop. On GUI applications, - for example this may be desired to use to run the client under the - GUI application's main loop. Typically the GUI application would - register an idle task that calls this function multiple times in - a second to quickly process the SILC specific data. */ -void silc_client_run_one(SilcClient client) +/**************************** Packet Processing *****************************/ + +/* Received new ID from server during registering to SILC network */ + +SILC_FSM_STATE(silc_client_new_id) { - /* Run the scheduler once. */ - silc_schedule_one(client->schedule, 0); + SilcClientConnection conn = fsm_context; + SilcClient client = conn->client; + SilcPacket packet = state_context; + SilcID id; + + if (conn->local_id) + goto out; + + SILC_LOG_DEBUG(("New ID received from server")); + + if (!silc_id_payload_parse_id(silc_buffer_data(&packet->buffer), + silc_buffer_len(&packet->buffer), &id)) + goto out; + + /* Create local client entry */ + conn->local_entry = silc_client_add_client(client, conn, (client->nickname ? + client->nickname : + client->username), + client->username, + client->realname, + &id.u.client_id, 0); + if (!conn->local_entry) + goto out; + + /* Save the ID */ + conn->local_id = &conn->local_entry->id; + conn->local_idp = silc_buffer_copy(&packet->buffer); + + /* Save cache entry */ + silc_idcache_find_by_id_one(conn->internal->client_cache, conn->local_id, + &conn->internal->local_entry); + + /* Save remote ID */ + if (packet->src_id_len) { + conn->remote_idp = silc_id_payload_encode_data(packet->src_id, + packet->src_id_len, + packet->src_id_type); + if (!conn->remote_idp) + goto out; + silc_id_payload_parse_id(silc_buffer_data(conn->remote_idp), + silc_buffer_len(conn->remote_idp), + &conn->remote_id); + } + + /* Signal connection that new ID was received so it can continue + with the registering. */ + if (conn->internal->registering) + silc_fsm_continue_sync(&conn->internal->event_thread); + + out: + /** Packet processed */ + silc_packet_free(packet); + silc_fsm_next(fsm, silc_client_connection_st_packet); + return SILC_FSM_CONTINUE; } + +/******************************* Public API *********************************/ + /* Allocates and adds new connection to the client. This adds the allocated connection to the connection table and returns a pointer to it. A client can have multiple connections to multiple servers. Every connection must @@ -237,30 +383,49 @@ void silc_client_run_one(SilcClient client) SilcClientConnection silc_client_add_connection(SilcClient client, - SilcClientConnectionParams *params, - char *hostname, int port, void *context) + SilcConnectionType conn_type, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + char *remote_host, int port, + SilcClientConnectCallback callback, + void *context) { SilcClientConnection conn; - int i; + SilcFSMThread thread; - SILC_LOG_DEBUG(("Adding new connection to %s:%d", hostname, port)); + if (!callback) + return NULL; + + SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port)); 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; + } - /* Initialize ID caches */ conn->client = client; - conn->remote_host = strdup(hostname); - conn->remote_port = port; + conn->public_key = public_key; + conn->private_key = private_key; + conn->remote_host = strdup(remote_host); + conn->remote_port = port ? port : 706; + conn->type = conn_type; + conn->callback = callback; conn->context = context; conn->internal->client_cache = - silc_idcache_alloc(0, SILC_ID_CLIENT, NULL, NULL, FALSE, TRUE); + silc_idcache_alloc(0, SILC_ID_CLIENT, NULL, NULL); conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL, - NULL, FALSE, TRUE); + NULL); conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL, - NULL, FALSE, TRUE); - conn->internal->pending_commands = silc_dlist_init(); + NULL); conn->internal->ftp_sessions = silc_dlist_init(); + conn->internal->verbose = TRUE; + silc_list_init(conn->internal->pending_commands, + struct SilcClientCommandContextStruct, next); if (params) { if (params->detach_data) @@ -270,18 +435,19 @@ silc_client_add_connection(SilcClient client, conn->internal->params.detach_data_len = params->detach_data_len; } - /* Add the connection to connections table */ - for (i = 0; i < client->internal->conns_count; i++) - if (client->internal->conns && !client->internal->conns[i]) { - client->internal->conns[i] = conn; - return conn; - } + /* Add the connection to connections list */ + // silc_dlist_add(client->internal->conns, conn); - client->internal->conns = - silc_realloc(client->internal->conns, sizeof(*client->internal->conns) - * (client->internal->conns_count + 1)); - client->internal->conns[client->internal->conns_count] = conn; - client->internal->conns_count++; + /* 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, + silc_client_fsm_destructor, NULL, + client->internal->params->threads); + if (!thread) { + silc_client_del_connection(client, conn); + return NULL; + } + silc_fsm_start_sync(thread, silc_client_connection_st_start); return conn; } @@ -290,266 +456,125 @@ silc_client_add_connection(SilcClient client, void silc_client_del_connection(SilcClient client, SilcClientConnection conn) { - int i; - - for (i = 0; i < client->internal->conns_count; i++) - if (client->internal->conns[i] == conn) { - /* Free all cache entries */ - SilcIDCacheList list; - SilcIDCacheEntry entry; - SilcClientCommandPending *r; - SilcBool ret; - - 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); +#if 0 + SilcClientConnection c; + SilcIDCacheList 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); + } - /* 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 (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_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 (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_free(conn->internal); - memset(conn, 0, sizeof(*conn)); - silc_free(conn); - - client->internal->conns[i] = NULL; + silc_idcache_list_free(list); } -} - -/* Adds listener socket to the listener sockets table. This function is - used to add socket objects that are listeners to the client. This should - not be used to add other connection objects. */ - -void silc_client_add_socket(SilcClient client, SilcSocketConnection sock) -{ - int i; - if (!client->internal->sockets) { - client->internal->sockets = - silc_calloc(1, sizeof(*client->internal->sockets)); - client->internal->sockets[0] = silc_socket_dup(sock); - client->internal->sockets_count = 1; - return; - } + /* 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); - for (i = 0; i < client->internal->sockets_count; i++) { - if (client->internal->sockets[i] == NULL) { - client->internal->sockets[i] = silc_socket_dup(sock); - return; + /* 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; } - } - - client->internal->sockets = - silc_realloc(client->internal->sockets, - sizeof(*client->internal->sockets) * - (client->internal->sockets_count + 1)); - client->internal->sockets[client->internal->sockets_count] = - silc_socket_dup(sock); - client->internal->sockets_count++; -} -/* Deletes listener socket from the listener sockets table. */ + silc_client_ftp_free_sessions(client, conn); -void silc_client_del_socket(SilcClient client, SilcSocketConnection sock) -{ - int i; + 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 (!client->internal->sockets) - return; + silc_free(conn->internal); + memset(conn, 0, sizeof(*conn)); + silc_free(conn); - for (i = 0; i < client->internal->sockets_count; i++) { - if (client->internal->sockets[i] == sock) { - silc_socket_free(sock); - client->internal->sockets[i] = NULL; - return; - } + silc_dlist_del(client->internal->conns, conn); } -} - -static int -silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx) -{ - int sock; - - /* XXX In the future we should give up this non-blocking connect all - together and use threads instead. */ - /* Create connection to server asynchronously */ - sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host); - if (sock < 0) - return -1; - - /* Register task that will receive the async connect and will - read the result. */ - ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, - silc_client_connect_to_server_start, - (void *)ctx, 0, 0, - SILC_TASK_FD, - SILC_TASK_PRI_NORMAL); - silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE, - FALSE); - - ctx->sock = sock; - - return sock; +#endif /* 0 */ } /* Connects to remote server. This is the main routine used to connect - to SILC server. Returns -1 on error and the created socket otherwise. - The `context' is user context that is saved into the SilcClientConnection - that is created after the connection is created. Note that application - may handle the connecting process outside the library. If this is the - case then this function is not used at all. When the connecting is - done the `connect' client operation is called. */ - -int silc_client_connect_to_server(SilcClient client, - SilcClientConnectionParams *params, - int port, char *host, void *context) + to remote SILC server. Returns FALSE on error. */ + +void silc_client_connect_to_server(SilcClient client, + SilcClientConnectionParams *params, + SilcPublicKey public_key, + SilcPrivateKey private_key, + char *remote_host, int port, + SilcClientConnectCallback callback, + void *context) { - SilcClientInternalConnectContext *ctx; SilcClientConnection conn; - int sock; - - SILC_LOG_DEBUG(("Connecting to port %d of server %s", - port, host)); - - conn = silc_client_add_connection(client, params, host, port, context); - - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, - "Connecting to port %d of server %s", port, host); - - /* Allocate internal context for connection process. This is - needed as we are doing async connecting. */ - ctx = silc_calloc(1, sizeof(*ctx)); - ctx->client = client; - ctx->conn = conn; - ctx->host = strdup(host); - ctx->port = port ? port : 706; - ctx->tries = 0; - - /* Do the actual connecting process */ - sock = silc_client_connect_to_server_internal(ctx); - if (sock == -1) - silc_client_del_connection(client, conn); - return sock; -} - -/* Socket hostname and IP lookup callback that is called before actually - starting the key exchange. The lookup is called from the function - silc_client_start_key_exchange. */ - -static void silc_client_start_key_exchange_cb(SilcSocketConnection sock, - void *context) -{ - SilcClientConnection conn = (SilcClientConnection)context; - SilcClient client = conn->client; - SilcProtocol protocol; - SilcClientKEInternalContext *proto_ctx; - - SILC_LOG_DEBUG(("Start")); - if (conn->sock->hostname) { - silc_free(conn->remote_host); - conn->remote_host = strdup(conn->sock->hostname); - } else { - conn->sock->hostname = strdup(conn->remote_host); - } - if (!conn->sock->ip) - conn->sock->ip = strdup(conn->sock->hostname); - conn->sock->port = conn->remote_port; + if (!client || !remote_host) + return; - /* Allocate internal Key Exchange context. This is sent to the - protocol as context. */ - proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); - proto_ctx->client = (void *)client; - proto_ctx->sock = silc_socket_dup(conn->sock); - proto_ctx->rng = client->rng; - proto_ctx->responder = FALSE; - proto_ctx->send_packet = silc_client_protocol_ke_send_packet; - proto_ctx->verify = silc_client_protocol_ke_verify_key; - - /* Perform key exchange protocol. silc_client_connect_to_server_final - will be called after the protocol is finished. */ - silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, - &protocol, (void *)proto_ctx, - silc_client_connect_to_server_second); - if (!protocol) { - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Error: Could not start key exchange protocol"); - silc_net_close_connection(conn->sock->sock); - client->internal->ops->connected(client, conn, SILC_CLIENT_CONN_ERROR); + /* Add new connection */ + conn = silc_client_add_connection(client, SILC_CONN_SERVER, params, + public_key, private_key, remote_host, + port, callback, context); + if (!conn) { + callback(client, NULL, SILC_CLIENT_CONN_ERROR, context); return; } - conn->sock->protocol = protocol; - /* Register the connection for network input and output. This sets - that scheduler will listen for incoming packets for this connection - and sets that outgoing packets may be sent to this connection as well. - However, this doesn't set the scheduler for outgoing traffic, it will - be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT, - later when outgoing data is available. */ - context = (void *)client; - SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(conn->sock->sock); + client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, + "Connecting to port %d of server %s", + port, remote_host); - /* Execute the protocol */ - silc_protocol_execute(protocol, client->schedule, 0, 0); + /* Signal connection machine to start connecting */ + conn->internal->connect = TRUE; } /* Start SILC Key Exchange (SKE) protocol to negotiate shared secret @@ -561,188 +586,22 @@ static void silc_client_start_key_exchange_cb(SilcSocketConnection sock, void silc_client_start_key_exchange(SilcClient client, SilcClientConnection conn, - int fd) + SilcStream stream) { - assert(client->pkcs); +#if 0 + assert(conn && stream); assert(client->public_key); assert(client->private_key); - /* Allocate new socket connection object */ - silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock); - - /* Sometimes when doing quick reconnects the new socket may be same as - the old one and there might be pending stuff for the old socket. - If new one is same then those pending sutff might cause problems. - Make sure they do not do that. */ - silc_schedule_task_del_by_fd(client->schedule, fd); - conn->nickname = (client->nickname ? strdup(client->nickname) : strdup(client->username)); +#endif /* 0 */ - /* Resolve the remote hostname and IP address for our socket connection */ - silc_socket_host_lookup(conn->sock, FALSE, silc_client_start_key_exchange_cb, - conn, client->schedule); -} - -/* Callback called when error has occurred during connecting (KE) to - the server. The `connect' client operation will be called. */ - -SILC_TASK_CALLBACK(silc_client_connect_failure) -{ - SilcClientKEInternalContext *ctx = - (SilcClientKEInternalContext *)context; - SilcClient client = (SilcClient)ctx->client; - - client->internal->ops->connected(client, ctx->sock->user_data, - SILC_CLIENT_CONN_ERROR_KE); - if (ctx->packet) - silc_packet_context_free(ctx->packet); - silc_free(ctx); -} - -/* Callback called when error has occurred during connecting (auth) to - the server. The `connect' client operation will be called. */ - -SILC_TASK_CALLBACK(silc_client_connect_failure_auth) -{ - SilcClientConnAuthInternalContext *ctx = - (SilcClientConnAuthInternalContext *)context; - SilcClient client = (SilcClient)ctx->client; - - client->internal->ops->connected(client, ctx->sock->user_data, ctx->status); - silc_free(ctx); -} - -/* Start of the connection to the remote server. This is called after - succesful TCP/IP connection has been established to the remote host. */ - -SILC_TASK_CALLBACK(silc_client_connect_to_server_start) -{ - SilcClientInternalConnectContext *ctx = - (SilcClientInternalConnectContext *)context; - SilcClient client = ctx->client; - SilcClientConnection conn = ctx->conn; - int opt, opt_len = sizeof(opt); - - SILC_LOG_DEBUG(("Start")); - - /* Check the socket status as it might be in error */ - silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len); - if (opt != 0) { - if (ctx->tries < 2) { - /* Connection failed but lets try again */ - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Could not connect to server %s: %s", - ctx->host, strerror(opt)); - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, - "Connecting to port %d of server %s resumed", - ctx->port, ctx->host); - - /* Unregister old connection try */ - silc_schedule_unset_listen_fd(client->schedule, fd); - silc_net_close_connection(fd); - silc_schedule_task_del(client->schedule, ctx->task); - - /* Try again */ - silc_client_connect_to_server_internal(ctx); - ctx->tries++; - } else { - /* Connection failed and we won't try anymore */ - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Could not connect to server %s: %s", - ctx->host, strerror(opt)); - silc_schedule_unset_listen_fd(client->schedule, fd); - silc_net_close_connection(fd); - silc_schedule_task_del(client->schedule, ctx->task); - silc_free(ctx); - - /* Notify application of failure */ - client->internal->ops->connected(client, conn, - SILC_CLIENT_CONN_ERROR_TIMEOUT); - } - return; - } - - silc_schedule_unset_listen_fd(client->schedule, fd); - silc_schedule_task_del(client->schedule, ctx->task); - silc_free(ctx); - - silc_client_start_key_exchange(client, conn, fd); -} - -/* Second part of the connecting to the server. This executed - authentication protocol. */ - -SILC_TASK_CALLBACK(silc_client_connect_to_server_second) -{ - SilcProtocol protocol = (SilcProtocol)context; - SilcClientKEInternalContext *ctx = - (SilcClientKEInternalContext *)protocol->context; - SilcClient client = (SilcClient)ctx->client; - SilcSocketConnection sock = NULL; - SilcClientConnAuthInternalContext *proto_ctx; - - 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 KE protocol")); - silc_protocol_free(protocol); - silc_ske_free_key_material(ctx->keymat); - if (ctx->ske) - silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); - ctx->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, ctx, - 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - return; - } - - /* We now have the key material as the result of the key exchange - protocol. Take the key material into use. Free the raw key material - as soon as we've set them into use. */ - silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat, - ctx->ske->prop->cipher, - ctx->ske->prop->pkcs, - ctx->ske->prop->hash, - ctx->ske->prop->hmac, - ctx->ske->prop->group, - ctx->responder); - silc_ske_free_key_material(ctx->keymat); - - /* Allocate internal context for the authentication protocol. This - is sent as context for the protocol. */ - proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); - proto_ctx->client = (void *)client; - proto_ctx->sock = sock = ctx->sock; - proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */ - proto_ctx->dest_id_type = ctx->dest_id_type; - proto_ctx->dest_id = ctx->dest_id; - - /* Free old protocol as it is finished now */ - silc_protocol_free(protocol); - if (ctx->packet) - silc_packet_context_free(ctx->packet); - ctx->packet = NULL; - silc_free(ctx); - sock->protocol = NULL; + /* Start */ - /* Resolve the authentication method to be used in this connection. The - completion callback is called after the application has resolved - the authentication method. */ - client->internal->ops->get_auth_method(client, sock->user_data, - sock->hostname, - sock->port, - silc_client_resolve_auth_method, - proto_ctx); } +#if 0 /* Authentication method resolving callback. Application calls this function after we've called the client->internal->ops->get_auth_method client operation to resolve the authentication method. We will continue @@ -922,51 +781,13 @@ SILC_TASK_CALLBACK(silc_client_connect_to_server_final) 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); } -/* Internal routine that sends packet or marks packet to be sent. This - is used directly only in special cases. Normal cases should use - silc_server_packet_send. Returns < 0 on error. */ - -int silc_client_packet_send_real(SilcClient client, - SilcSocketConnection sock, - SilcBool force_send) -{ - int ret; - - /* If rekey protocol is active we must assure that all packets are - sent through packet queue. */ - if (SILC_CLIENT_IS_REKEY(sock)) - force_send = FALSE; - - /* If outbound data is already pending do not force send */ - if (SILC_IS_OUTBUF_PENDING(sock)) - force_send = FALSE; - - /* Send the packet */ - ret = silc_packet_send(sock, force_send); - if (ret != -2) - return ret; - - /* Mark that there is some outgoing data available for this connection. - This call sets the connection both for input and output (the input - is set always and this call keeps the input setting, actually). - Actual data sending is performed by silc_client_packet_process. */ - SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(client->schedule, sock->sock); - - /* Mark to socket that data is pending in outgoing buffer. This flag - is needed if new data is added to the buffer before the earlier - put data is sent to the network. */ - SILC_SET_OUTBUF_PENDING(sock); - - return 0; -} - /* Packet processing callback. This is used to send and receive packets from network. This is generic task. */ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process) { SilcClient client = (SilcClient)context; - SilcSocketConnection sock = NULL; + SilcClientConnection conn = NULL; SilcClientConnection conn; int ret; @@ -1043,507 +864,72 @@ SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process) else silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, silc_client_packet_parse, client); - } -} - -/* Parser callback called by silc_packet_receive_process. Thie merely - registers timeout that will handle the actual parsing when appropriate. */ - -static SilcBool silc_client_packet_parse(SilcPacketParserContext *parser_context, - void *context) -{ - SilcClient client = (SilcClient)context; - SilcSocketConnection sock = parser_context->sock; - SilcClientConnection conn = (SilcClientConnection)sock->user_data; - SilcPacketContext *packet = parser_context->packet; - SilcPacketType ret; - - if (conn && conn->internal->hmac_receive && conn->sock == sock) - conn->internal->psn_receive = parser_context->packet->sequence + 1; - - /* Parse the packet immediately */ - if (parser_context->normal) - ret = silc_packet_parse(packet, conn->internal->receive_key); - else - ret = silc_packet_parse_special(packet, conn->internal->receive_key); - - if (ret == SILC_PACKET_NONE) { - silc_packet_context_free(packet); - silc_free(parser_context); - return FALSE; - } - - /* If protocol for this connection is key exchange or rekey then we'll - process all packets synchronously, since there might be packets in - queue that we are not able to decrypt without first processing the - packets before them. */ - if (sock->protocol && sock->protocol->protocol && - (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE || - sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) { - - /* Parse the incoming packet type */ - silc_client_packet_parse_type(client, sock, packet); - - /* Reprocess the buffer since we'll return FALSE. This is because - the `conn->internal->receive_key' might have become valid by processing - the previous packet */ - if (sock->type != SILC_SOCKET_TYPE_UNKNOWN) - silc_packet_receive_process(sock, FALSE, conn->internal->receive_key, - conn->internal->hmac_receive, - conn->internal->psn_receive, - silc_client_packet_parse, client); - else - silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, - silc_client_packet_parse, client); - - silc_packet_context_free(packet); - silc_free(parser_context); - - return FALSE; - } - - /* Parse the incoming packet type */ - silc_client_packet_parse_type(client, sock, packet); - silc_packet_context_free(packet); - silc_free(parser_context); - return TRUE; -} - -/* Parses the packet type and calls what ever routines the packet type - requires. This is done for all incoming packets. */ - -void silc_client_packet_parse_type(SilcClient client, - SilcSocketConnection sock, - SilcPacketContext *packet) -{ - SilcBuffer buffer = packet->buffer; - SilcPacketType type = packet->type; - - SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(type))); - - /* Parse the packet type */ - switch(type) { - - case SILC_PACKET_DISCONNECT: - silc_client_disconnected_by_server(client, sock, buffer); - break; - - case SILC_PACKET_SUCCESS: - /* - * Success received for something. For now we can have only - * one protocol for connection executing at once hence this - * success message is for whatever protocol is executing currently. - */ - if (sock->protocol) - silc_protocol_execute(sock->protocol, client->schedule, 0, 0); - break; - - case SILC_PACKET_FAILURE: - /* - * Failure received for some protocol. Set the protocol state to - * error and call the protocol callback. This fill cause error on - * protocol and it will call the final callback. - */ - silc_client_process_failure(client, sock, packet); - break; - - case SILC_PACKET_REJECT: - break; - - case SILC_PACKET_NOTIFY: - /* - * Received notify message - */ - silc_client_notify_by_server(client, sock, packet); - break; - - case SILC_PACKET_ERROR: - /* - * Received error message - */ - silc_client_error_by_server(client, sock, buffer); - break; - - case SILC_PACKET_CHANNEL_MESSAGE: - /* - * Received message to (from, actually) a channel - */ - silc_client_channel_message(client, sock, packet); - break; - - case SILC_PACKET_CHANNEL_KEY: - /* - * Received key for a channel. By receiving this key the client will be - * able to talk to the channel it has just joined. This can also be - * a new key for existing channel as keys expire peridiocally. - */ - silc_client_receive_channel_key(client, sock, buffer); - break; - - case SILC_PACKET_PRIVATE_MESSAGE: - /* - * Received private message - */ - silc_client_private_message(client, sock, packet); - break; - - case SILC_PACKET_PRIVATE_MESSAGE_KEY: - /* - * Received private message key indicator - */ - silc_client_private_message_key(client, sock, packet); - break; - - case SILC_PACKET_COMMAND: - /* - * Received command packet, a special case since normally client - * does not receive commands. - */ - silc_client_command_process(client, sock, packet); - break; - - case SILC_PACKET_COMMAND_REPLY: - /* - * Recived reply for a command - */ - silc_client_command_reply_process(client, sock, packet); - break; - - case SILC_PACKET_KEY_EXCHANGE: - if (sock->protocol && sock->protocol->protocol && - sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) { - SilcClientKEInternalContext *proto_ctx = - (SilcClientKEInternalContext *)sock->protocol->context; - - proto_ctx->packet = silc_packet_context_dup(packet); - proto_ctx->dest_id_type = packet->src_id_type; - proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len, - packet->src_id_type); - if (!proto_ctx->dest_id) - break; - - /* Let the protocol handle the packet */ - silc_protocol_execute(sock->protocol, client->schedule, 0, 0); - } else { - SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange " - "protocol active, packet dropped.")); - } - break; - - case SILC_PACKET_KEY_EXCHANGE_1: - if (sock->protocol && sock->protocol->protocol && - (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE || - sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) { - - if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) { - SilcClientRekeyInternalContext *proto_ctx = - (SilcClientRekeyInternalContext *)sock->protocol->context; - - if (proto_ctx->packet) - silc_packet_context_free(proto_ctx->packet); - - proto_ctx->packet = silc_packet_context_dup(packet); - - /* Let the protocol handle the packet */ - silc_protocol_execute(sock->protocol, client->schedule, 0, 0); - } else { - SilcClientKEInternalContext *proto_ctx = - (SilcClientKEInternalContext *)sock->protocol->context; - - if (proto_ctx->packet) - silc_packet_context_free(proto_ctx->packet); - - proto_ctx->packet = silc_packet_context_dup(packet); - proto_ctx->dest_id_type = packet->src_id_type; - proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len, - packet->src_id_type); - if (!proto_ctx->dest_id) - break; - - /* Let the protocol handle the packet */ - silc_protocol_execute(sock->protocol, client->schedule, 0, 0); - } - } else { - SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange " - "protocol active, packet dropped.")); - } - break; - - case SILC_PACKET_KEY_EXCHANGE_2: - if (sock->protocol && sock->protocol->protocol && - (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE || - sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) { - - if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) { - SilcClientRekeyInternalContext *proto_ctx = - (SilcClientRekeyInternalContext *)sock->protocol->context; - - if (proto_ctx->packet) - silc_packet_context_free(proto_ctx->packet); - - proto_ctx->packet = silc_packet_context_dup(packet); - - /* Let the protocol handle the packet */ - silc_protocol_execute(sock->protocol, client->schedule, 0, 0); - } else { - SilcClientKEInternalContext *proto_ctx = - (SilcClientKEInternalContext *)sock->protocol->context; - - if (proto_ctx->packet) - silc_packet_context_free(proto_ctx->packet); - if (proto_ctx->dest_id) - silc_free(proto_ctx->dest_id); - proto_ctx->packet = silc_packet_context_dup(packet); - proto_ctx->dest_id_type = packet->src_id_type; - proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len, - packet->src_id_type); - if (!proto_ctx->dest_id) - break; - - /* Let the protocol handle the packet */ - silc_protocol_execute(sock->protocol, client->schedule, 0, 0); - } - } else { - SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange " - "protocol active, packet dropped.")); - } - break; - - case SILC_PACKET_NEW_ID: - { - /* - * Received new ID from server. This packet is received at - * the connection to the server. New ID is also received when - * user changes nickname but in that case the new ID is received - * as command reply and not as this packet type. - */ - SilcIDPayload idp; - - idp = silc_id_payload_parse(buffer->data, buffer->len); - if (!idp) - break; - if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT) - break; - - silc_client_receive_new_id(client, sock, idp); - silc_id_payload_free(idp); - break; - } - - case SILC_PACKET_HEARTBEAT: - /* - * Received heartbeat packet - */ - SILC_LOG_DEBUG(("Heartbeat packet")); - break; - - case SILC_PACKET_KEY_AGREEMENT: - /* - * Received key agreement packet - */ - SILC_LOG_DEBUG(("Key agreement packet")); - silc_client_key_agreement(client, sock, packet); - break; - - case SILC_PACKET_REKEY: - SILC_LOG_DEBUG(("Re-key packet")); - /* We ignore this for now */ - break; - - case SILC_PACKET_REKEY_DONE: - SILC_LOG_DEBUG(("Re-key done packet")); - - if (sock->protocol && sock->protocol->protocol && - sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) { - - SilcClientRekeyInternalContext *proto_ctx = - (SilcClientRekeyInternalContext *)sock->protocol->context; - - if (proto_ctx->packet) - silc_packet_context_free(proto_ctx->packet); - - proto_ctx->packet = silc_packet_context_dup(packet); - - /* Let the protocol handle the packet */ - if (proto_ctx->responder == FALSE) - silc_protocol_execute(sock->protocol, client->schedule, 0, 0); - else - /* Let the protocol handle the packet */ - silc_protocol_execute(sock->protocol, client->schedule, - 0, 100000); - } else { - SILC_LOG_ERROR(("Received Re-key done packet but no re-key " - "protocol active, packet dropped.")); - } - break; - - case SILC_PACKET_CONNECTION_AUTH_REQUEST: - /* - * Reveived reply to our connection authentication method request - * packet. This is used to resolve the authentication method for the - * current session from the server if the client does not know it. - */ - silc_client_connection_auth_request(client, sock, packet); - break; - - case SILC_PACKET_FTP: - /* Received file transfer packet. */ - silc_client_ftp(client, sock, packet); - break; - - default: - SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type)); - break; - } -} - -/* Sends packet. This doesn't actually send the packet instead it assembles - it and marks it to be sent. However, if force_send is TRUE the packet - is sent immediately. if dst_id, cipher and hmac are NULL those parameters - will be derived from sock argument. Otherwise the valid arguments sent - are used. */ - -void silc_client_packet_send(SilcClient client, - SilcSocketConnection sock, - SilcPacketType type, - void *dst_id, - SilcIdType dst_id_type, - SilcCipher cipher, - SilcHmac hmac, - unsigned char *data, - SilcUInt32 data_len, - SilcBool force_send) -{ - SilcPacketContext packetdata; - const SilcBufferStruct packet; - int block_len; - SilcUInt32 sequence = 0; - - if (!sock) - return; - - SILC_LOG_DEBUG(("Sending packet, type %d", type)); - - /* Get data used in the packet sending, keys and stuff */ - if ((!cipher || !hmac || !dst_id) && sock->user_data) { - if (!cipher && ((SilcClientConnection)sock->user_data)->internal->send_key) - cipher = ((SilcClientConnection)sock->user_data)->internal->send_key; - - if (!hmac && ((SilcClientConnection)sock->user_data)->internal->hmac_send) - hmac = ((SilcClientConnection)sock->user_data)->internal->hmac_send; + } +} - if (!dst_id && ((SilcClientConnection)sock->user_data)->remote_id) { - dst_id = ((SilcClientConnection)sock->user_data)->remote_id; - dst_id_type = SILC_ID_SERVER; - } +/* Parser callback called by silc_packet_receive_process. Thie merely + registers timeout that will handle the actual parsing when appropriate. */ - if (hmac) - sequence = ((SilcClientConnection)sock->user_data)->internal->psn_send++; +static SilcBool silc_client_packet_parse(SilcPacketParserContext *parser_context, + void *context) +{ + SilcClient client = (SilcClient)context; + SilcClientConnection conn = parser_context->sock; + SilcClientConnection conn = (SilcClientConnection)sock->user_data; + SilcPacketContext *packet = parser_context->packet; + SilcPacketType ret; - /* Check for mandatory rekey */ - if (sequence == SILC_CLIENT_REKEY_THRESHOLD) - silc_schedule_task_add(client->schedule, sock->sock, - silc_client_rekey_callback, sock, 0, 1, - SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - } + if (conn && conn->internal->hmac_receive && conn->sock == sock) + conn->internal->psn_receive = parser_context->packet->sequence + 1; - block_len = cipher ? silc_cipher_get_block_len(cipher) : 0; - - /* Set the packet context pointers */ - packetdata.flags = 0; - packetdata.type = type; - if (sock->user_data && - ((SilcClientConnection)sock->user_data)->local_id_data) { - packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data; - packetdata.src_id_len = - silc_id_get_len(((SilcClientConnection)sock->user_data)->local_id, - SILC_ID_CLIENT); - } else { - packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char)); - packetdata.src_id_len = SILC_ID_CLIENT_LEN; - } - packetdata.src_id_type = SILC_ID_CLIENT; - if (dst_id) { - packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type); - packetdata.dst_id_len = silc_id_get_len(dst_id, dst_id_type); - packetdata.dst_id_type = dst_id_type; - } else { - packetdata.dst_id = NULL; - packetdata.dst_id_len = 0; - packetdata.dst_id_type = SILC_ID_NONE; - } - data_len = SILC_PACKET_DATALEN(data_len, (SILC_PACKET_HEADER_LEN + - packetdata.src_id_len + - packetdata.dst_id_len)); - packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + - packetdata.src_id_len + packetdata.dst_id_len; - if (type == SILC_PACKET_CONNECTION_AUTH) - SILC_PACKET_PADLEN_MAX(packetdata.truelen, block_len, packetdata.padlen); + /* Parse the packet immediately */ + if (parser_context->normal) + ret = silc_packet_parse(packet, conn->internal->receive_key); else - SILC_PACKET_PADLEN(packetdata.truelen, block_len, packetdata.padlen); + ret = silc_packet_parse_special(packet, conn->internal->receive_key); - /* Create the outgoing packet */ - if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock, - data, data_len, (const SilcBuffer)&packet)) { - SILC_LOG_ERROR(("Error assembling packet")); - return; + if (ret == SILC_PACKET_NONE) { + silc_packet_context_free(packet); + silc_free(parser_context); + return FALSE; } - /* Encrypt the packet */ - if (cipher) - silc_packet_encrypt(cipher, hmac, sequence, (SilcBuffer)&packet, - packet.len); - - SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, packet.len), - packet.data, packet.len); + /* If protocol for this connection is key exchange or rekey then we'll + process all packets synchronously, since there might be packets in + queue that we are not able to decrypt without first processing the + packets before them. */ + if (sock->protocol && sock->protocol->protocol && + (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE || + sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) { - /* Now actually send the packet */ - silc_client_packet_send_real(client, sock, force_send); -} + /* Parse the incoming packet type */ + silc_client_packet_parse_type(client, sock, packet); -/* Packet sending routine for application. This is the only routine that - is provided for application to send SILC packets. */ + /* Reprocess the buffer since we'll return FALSE. This is because + the `conn->internal->receive_key' might have become valid by processing + the previous packet */ + if (sock->type != SILC_SOCKET_TYPE_UNKNOWN) + silc_packet_receive_process(sock, FALSE, conn->internal->receive_key, + conn->internal->hmac_receive, + conn->internal->psn_receive, + silc_client_packet_parse, client); + else + silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, + silc_client_packet_parse, client); -SilcBool silc_client_send_packet(SilcClient client, - SilcClientConnection conn, - SilcPacketType type, - const unsigned char *data, - SilcUInt32 data_len) -{ + silc_packet_context_free(packet); + silc_free(parser_context); - assert(client); - if (!conn) return FALSE; + } - silc_client_packet_send(client, conn->sock, type, NULL, 0, NULL, NULL, - (unsigned char *)data, data_len, TRUE); + /* Parse the incoming packet type */ + silc_client_packet_parse_type(client, sock, packet); + silc_packet_context_free(packet); + silc_free(parser_context); return TRUE; } - -void silc_client_packet_queue_purge(SilcClient client, - SilcSocketConnection sock) -{ - if (sock && SILC_IS_OUTBUF_PENDING(sock) && - !(SILC_IS_DISCONNECTED(sock))) { - int ret; - - ret = silc_packet_send(sock, TRUE); - if (ret == -2) { - if (sock->outbuf && sock->outbuf->len > 0) { - /* Couldn't send all data, put the queue back up, we'll send - rest later. */ - SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(client->schedule, sock->sock); - SILC_SET_OUTBUF_PENDING(sock); - return; - } - } - - /* Purged all data */ - SILC_UNSET_OUTBUF_PENDING(sock); - SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock); - silc_buffer_clear(sock->outbuf); - } -} +#endif /* 0 */ /* Closes connection to remote end. Free's all allocated data except for some information such as nickname etc. that are valid at all time. @@ -1553,8 +939,8 @@ void silc_client_packet_queue_purge(SilcClient client, connection but `conn->sock' might be actually a different connection than the `sock'). */ +#if 0 void silc_client_close_connection_real(SilcClient client, - SilcSocketConnection sock, SilcClientConnection conn) { int del = FALSE; @@ -1614,32 +1000,16 @@ void silc_client_close_connection_real(SilcClient client, void silc_client_close_connection(SilcClient client, SilcClientConnection conn) { - silc_client_close_connection_real(client, NULL, conn); -} - -/* Called when we receive disconnection packet from server. This - closes our end properly and displays the reason of the disconnection - on the screen. */ - -SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later) -{ - SilcClient client = (SilcClient)context; - SilcSocketConnection sock; - - SILC_CLIENT_GET_SOCK(client, fd, sock); - if (sock == NULL) - return; - - silc_client_close_connection_real(client, sock, sock->user_data); + // silc_client_close_connection_real(client, NULL, conn); } /* Called when we receive disconnection packet from server. This closes our end properly and displays the reason of the disconnection on the screen. */ -void silc_client_disconnected_by_server(SilcClient client, - SilcSocketConnection sock, - SilcBuffer packet) +void silc_client_disconnect(SilcClient client, + SilcClientConnection conn, + SilcBuffer packet) { SilcClientConnection conn; SilcStatus status; @@ -1675,7 +1045,7 @@ void silc_client_disconnected_by_server(SilcClient client, We don't take any action what so ever of the error message. */ void silc_client_error_by_server(SilcClient client, - SilcSocketConnection sock, + SilcClientConnection conn, SilcBuffer message) { char *msg; @@ -1732,7 +1102,7 @@ static void silc_client_resume_session_cb(SilcClient client, deleted from cache and new one is added. */ void silc_client_receive_new_id(SilcClient client, - SilcSocketConnection sock, + SilcClientConnection conn, SilcIDPayload idp) { SilcClientConnection conn = (SilcClientConnection)sock->user_data; @@ -1791,6 +1161,7 @@ void silc_client_receive_new_id(SilcClient client, silc_idcache_add(conn->internal->client_cache, nickname, conn->local_id, (void *)conn->local_entry, 0, NULL); +#if 0 if (connecting) { SilcBuffer sidp; @@ -1835,6 +1206,7 @@ void silc_client_receive_new_id(SilcClient client, NULL); } } +#endif /* 0 */ } /* Removes a client entry from all channels it has joined. */ @@ -1886,7 +1258,7 @@ void silc_client_replace_from_channels(SilcClient client, with timeout. */ void silc_client_process_failure(SilcClient client, - SilcSocketConnection sock, + SilcClientConnection conn, SilcPacketContext *packet) { SilcUInt32 failure = 0; @@ -1906,7 +1278,7 @@ void silc_client_process_failure(SilcClient client, SILC_TASK_CALLBACK_GLOBAL(silc_client_rekey_callback) { - SilcSocketConnection sock = (SilcSocketConnection)context; + SilcClientConnection conn = (SilcSocketConnection)context; SilcClientConnection conn = (SilcClientConnection)sock->user_data; SilcClient client = (SilcClient)conn->internal->rekey->context; SilcProtocol protocol; @@ -1946,7 +1318,7 @@ SILC_TASK_CALLBACK(silc_client_rekey_final) SilcClientRekeyInternalContext *ctx = (SilcClientRekeyInternalContext *)protocol->context; SilcClient client = (SilcClient)ctx->client; - SilcSocketConnection sock = ctx->sock; + SilcClientConnection conn = ctx->sock; SilcClientConnection conn = (SilcClientConnection)sock->user_data; SILC_LOG_DEBUG(("Start")); @@ -1994,7 +1366,7 @@ SILC_TASK_CALLBACK(silc_client_rekey_final) client does not know it beforehand. */ void silc_client_connection_auth_request(SilcClient client, - SilcSocketConnection sock, + SilcClientConnection conn, SilcPacketContext *packet) { SilcClientConnection conn = (SilcClientConnection)sock->user_data; @@ -2093,3 +1465,199 @@ silc_client_request_authentication_method(SilcClient client, client->internal->params->connauth_request_secs, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); } +#endif /* 0 */ + + +/******************************* Client API *********************************/ + +/* 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 + and caller must free it. */ + +SilcClient silc_client_alloc(SilcClientOperations *ops, + SilcClientParams *params, + void *application, + const char *version_string) +{ + SilcClient new_client; + + new_client = silc_calloc(1, sizeof(*new_client)); + new_client->application = application; + + new_client->internal = silc_calloc(1, sizeof(*new_client->internal)); + new_client->internal->ops = ops; + new_client->internal->params = + silc_calloc(1, sizeof(*new_client->internal->params)); + if (!version_string) + version_string = silc_version_string; + new_client->internal->silc_client_version = strdup(version_string); + + 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; + + return new_client; +} + +/* Frees client object and its internals. */ + +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_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); + } +} + +/* 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) +{ + SILC_LOG_DEBUG(("Initializing client")); + + assert(client); + assert(client->username); + assert(client->hostname); + assert(client->realname); + + /* 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), + SILC_STRING_UTF8, 128)) { + SILC_LOG_ERROR(("Malformed username '%s'", client->username)); + return FALSE; + } + if (!silc_identifier_verify(client->hostname, strlen(client->hostname), + SILC_STRING_UTF8, 256)) { + SILC_LOG_ERROR(("Malformed hostname '%s'", client->hostname)); + return FALSE; + } + if (!silc_utf8_valid(client->realname, strlen(client->realname))) { + SILC_LOG_ERROR(("Malformed realname '%s'", client->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 + application might have registered earlier. */ + silc_cipher_register_default(); + silc_pkcs_register_default(); + silc_hash_register_default(); + 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(); + 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); + if (!client->schedule) + return FALSE; + + /* Start packet engine */ + client->internal->packet_engine = + silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs, + 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); + + 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. */ + +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); +} + +/* Call scheduler one iteration and return. This cannot be called if threads + are in use. */ + +void silc_client_run_one(SilcClient client) +{ + silc_schedule_one(client->schedule, -1); +}