From 598a79d875c80b1f6cbee2ae78198cbff46ab7dc Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Sun, 3 Dec 2006 21:03:06 +0000 Subject: [PATCH] More client library rewrites. --- lib/silcclient/client.c | 209 +++++++++++------------- lib/silcclient/client_channel.c | 39 ++++- lib/silcclient/client_channel.h | 11 +- lib/silcclient/client_connect.c | 18 ++- lib/silcclient/client_entry.c | 197 +++++++++++++---------- lib/silcclient/client_internal.h | 32 ++-- lib/silcclient/client_keyagr.c | 264 ++++++++++++++----------------- lib/silcclient/client_notify.c | 78 ++++----- lib/silcclient/client_prvmsg.c | 11 +- lib/silcclient/client_register.c | 66 ++++++-- lib/silcclient/command.c | 47 ++++-- lib/silcclient/command_reply.c | 22 +-- lib/silcclient/silcclient.h | 6 +- 13 files changed, 531 insertions(+), 469 deletions(-) diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 9a64a6c0..bd354495 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -293,6 +293,7 @@ SILC_FSM_STATE(silc_client_connection_st_packet) case SILC_PACKET_NEW_ID: /** New ID */ silc_fsm_next(fsm, silc_client_new_id); + break; case SILC_PACKET_CONNECTION_AUTH_REQUEST: /* Reply to connection authentication request to resolve authentication @@ -320,7 +321,9 @@ SILC_FSM_STATE(silc_client_connection_st_close) SILC_LOG_DEBUG(("Closing remote connection")); - /* XXX abort any ongoing events (protocols) */ + /* Abort ongoing events */ + if (conn->internal->op) + silc_async_abort(conn->internal->op, NULL, NULL); /* Close connection */ silc_packet_stream_destroy(conn->stream); @@ -457,6 +460,9 @@ 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); @@ -498,96 +504,66 @@ silc_client_add_connection(SilcClient client, return conn; } -/* Removes connection from client. Frees all memory. */ +/* Deletes connection. This is always called from the connection machine + destructor. Do not call this directly other places. */ void silc_client_del_connection(SilcClient client, SilcClientConnection conn) { -#if 0 - SilcClientConnection c; - SilcIDCacheList list; + SilcList list; SilcIDCacheEntry entry; - SilcClientCommandPending *r; - SilcBool ret; - - silc_dlist_start(client->internal->conns); - while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) { - if (c != conn) - continue; - - /* Free all cache entries */ - if (silc_idcache_get_all(conn->internal->client_cache, &list)) { - ret = silc_idcache_list_first(list, &entry); - while (ret) { - silc_client_del_client(client, conn, entry->context); - ret = silc_idcache_list_next(list, &entry); - } - silc_idcache_list_free(list); - } - - if (silc_idcache_get_all(conn->internal->channel_cache, &list)) { - ret = silc_idcache_list_first(list, &entry); - while (ret) { - silc_client_del_channel(client, conn, entry->context); - ret = silc_idcache_list_next(list, &entry); - } - silc_idcache_list_free(list); - } - - if (silc_idcache_get_all(conn->internal->server_cache, &list)) { - ret = silc_idcache_list_first(list, &entry); - while (ret) { - silc_client_del_server(client, conn, entry->context); - ret = silc_idcache_list_next(list, &entry); - } - silc_idcache_list_free(list); - } - - /* Clear ID caches */ - if (conn->internal->client_cache) - silc_idcache_free(conn->internal->client_cache); - if (conn->internal->channel_cache) - silc_idcache_free(conn->internal->channel_cache); - if (conn->internal->server_cache) - silc_idcache_free(conn->internal->server_cache); - - /* Free data (my ID is freed in above silc_client_del_client). - conn->nickname is freed when freeing the local_entry->nickname. */ - silc_free(conn->remote_host); - silc_free(conn->local_id_data); - if (conn->internal->send_key) - silc_cipher_free(conn->internal->send_key); - if (conn->internal->receive_key) - silc_cipher_free(conn->internal->receive_key); - if (conn->internal->hmac_send) - silc_hmac_free(conn->internal->hmac_send); - if (conn->internal->hmac_receive) - silc_hmac_free(conn->internal->hmac_receive); - silc_free(conn->internal->rekey); - - if (conn->internal->active_session) { - if (conn->sock) - conn->sock->user_data = NULL; - silc_client_ftp_session_free(conn->internal->active_session); - conn->internal->active_session = NULL; - } - - silc_client_ftp_free_sessions(client, conn); + SilcFSMThread thread; + SilcClientCommandContext cmd; - 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); - } + SILC_LOG_DEBUG(("Freeing connection %p", conn)); - silc_free(conn->internal); - memset(conn, 0, sizeof(*conn)); - silc_free(conn); - - silc_dlist_del(client->internal->conns, conn); + /* Free all cache entries */ + if (silc_idcache_get_all(conn->internal->client_cache, &list)) { + silc_list_start(list); + while ((entry = silc_list_get(list))) + silc_client_del_client(client, conn, entry->context); } -#endif /* 0 */ + if (silc_idcache_get_all(conn->internal->channel_cache, &list)) { + silc_list_start(list); + while ((entry = silc_list_get(list))) + silc_client_del_channel(client, conn, entry->context); + } + if (silc_idcache_get_all(conn->internal->server_cache, &list)) { + silc_list_start(list); + while ((entry = silc_list_get(list))) + silc_client_del_server(client, conn, entry->context); + } + + /* Free ID caches */ + if (conn->internal->client_cache) + silc_idcache_free(conn->internal->client_cache); + if (conn->internal->channel_cache) + silc_idcache_free(conn->internal->channel_cache); + if (conn->internal->server_cache) + silc_idcache_free(conn->internal->server_cache); + + /* Free thread pool */ + silc_list_start(conn->internal->thread_pool); + while ((thread = silc_list_get(conn->internal->thread_pool))) + silc_fsm_free(thread); + + /* Free pending commands */ + silc_list_start(conn->internal->pending_commands); + while ((cmd = silc_list_get(conn->internal->pending_commands))) + silc_client_command_free(cmd); + + silc_free(conn->remote_host); + silc_buffer_free(conn->internal->local_idp); + silc_buffer_free(conn->internal->remote_idp); + silc_mutex_free(conn->internal->lock); + if (conn->internal->hash) + silc_hash_free(conn->internal->hash); + if (conn->internal->sha1hash) + silc_hash_free(conn->internal->sha1hash); + silc_atomic_uninit16(&conn->internal->cmd_ident); + + silc_free(conn->internal); + memset(conn, 'F', sizeof(*conn)); + silc_free(conn); } @@ -653,10 +629,6 @@ SilcBool silc_client_connect_to_client(SilcClient client, return FALSE; } - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, - "Connecting to port %d of client host %s", - port, remote_host); - /* Signal connection machine to start connecting */ conn->internal->connect = TRUE; return TRUE; @@ -681,8 +653,11 @@ SilcBool silc_client_key_exchange(SilcClient client, if (!client || !stream) return FALSE; - if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) + if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) { + SILC_LOG_ERROR(("Socket stream does not have remote host name set")); + callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context); return FALSE; + } /* Add new connection */ conn = silc_client_add_connection(client, conn_type, params, @@ -704,7 +679,11 @@ SilcBool silc_client_key_exchange(SilcClient client, void silc_client_close_connection(SilcClient client, SilcClientConnection conn) { + SILC_LOG_DEBUG(("Closing connection %p", conn)); + /* Signal to close connection */ + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); } #if 0 @@ -1010,9 +989,6 @@ SilcClient silc_client_alloc(SilcClientOperations *ops, if (params) memcpy(new_client->internal->params, params, sizeof(*params)); - if (!new_client->internal->params->task_max) - new_client->internal->params->task_max = 200; - if (!new_client->internal->params->rekey_secs) new_client->internal->params->rekey_secs = 3600; @@ -1030,22 +1006,23 @@ SilcClient silc_client_alloc(SilcClientOperations *ops, void silc_client_free(SilcClient client) { - if (client) { - if (client->rng) - silc_rng_free(client->rng); - - if (!client->internal->params->dont_register_crypto_library) { - silc_cipher_unregister_all(); - silc_pkcs_unregister_all(); - silc_hash_unregister_all(); - silc_hmac_unregister_all(); - } + if (client->rng) + silc_rng_free(client->rng); - silc_free(client->internal->params); - silc_free(client->internal->silc_client_version); - silc_free(client->internal); - silc_free(client); + if (!client->internal->params->dont_register_crypto_library) { + silc_cipher_unregister_all(); + silc_pkcs_unregister_all(); + silc_hash_unregister_all(); + silc_hmac_unregister_all(); } + + silc_free(client->username); + silc_free(client->hostname); + silc_free(client->realname); + silc_free(client->internal->params); + silc_free(client->internal->silc_client_version); + silc_free(client->internal); + silc_free(client); } /* Initializes the client. This makes all the necessary steps to make @@ -1060,11 +1037,13 @@ SilcBool silc_client_init(SilcClient client, const char *username, if (!client) return FALSE; - if (!username || !hostname || !realname) { + if (!username || !hostname) { SILC_LOG_ERROR(("Username, hostname and realname must be given to " "silc_client_init")); return FALSE; } + if (!realname) + realname = username; /* Validate essential strings */ if (!silc_identifier_verify(username, strlen(username), @@ -1104,13 +1083,13 @@ SilcBool silc_client_init(SilcClient client, const char *username, /* 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); if (!client->schedule) return FALSE; @@ -1121,19 +1100,15 @@ 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 */ + silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule); + silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0); silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run); /* Signal the application when we are running */ diff --git a/lib/silcclient/client_channel.c b/lib/silcclient/client_channel.c index 37cd5a78..28cc6147 100644 --- a/lib/silcclient/client_channel.c +++ b/lib/silcclient/client_channel.c @@ -673,7 +673,9 @@ SilcChannelUser silc_client_on_channel(SilcChannelEntry channel, /* Adds client to channel. Returns TRUE if user was added or is already added to the channel, FALSE on error. */ -SilcBool silc_client_add_to_channel(SilcChannelEntry channel, +SilcBool silc_client_add_to_channel(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry channel, SilcClientEntry client_entry, SilcUInt32 cumode) { @@ -689,6 +691,10 @@ SilcBool silc_client_add_to_channel(SilcChannelEntry channel, chu->client = client_entry; chu->channel = channel; chu->mode = cumode; + + silc_client_ref_client(client, conn, client_entry); + silc_client_ref_channel(client, conn, channel); + silc_hash_table_add(channel->user_list, client_entry, chu); silc_hash_table_add(client_entry->channels, channel, chu); @@ -697,7 +703,9 @@ SilcBool silc_client_add_to_channel(SilcChannelEntry channel, /* Removes client from a channel */ -SilcBool silc_client_remove_from_channel(SilcChannelEntry channel, +SilcBool silc_client_remove_from_channel(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry channel, SilcClientEntry client_entry) { SilcChannelUser chu; @@ -710,6 +718,9 @@ SilcBool silc_client_remove_from_channel(SilcChannelEntry channel, silc_hash_table_del(chu->channel->user_list, chu->client); silc_free(chu); + silc_client_unref_client(client, conn, client_entry); + silc_client_unref_channel(client, conn, channel); + return TRUE; } @@ -726,8 +737,32 @@ void silc_client_remove_from_channels(SilcClient client, while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { silc_hash_table_del(chu->client->channels, chu->channel); silc_hash_table_del(chu->channel->user_list, chu->client); + silc_client_unref_client(client, conn, chu->client); + silc_client_unref_channel(client, conn, chu->channel); silc_free(chu); } silc_hash_table_list_reset(&htl); } + +/* Empties channel from users. */ + +void silc_client_empty_channel(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry channel) +{ + SilcHashTableList htl; + SilcChannelUser chu; + + silc_hash_table_list(channel->user_list, &htl); + while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { + silc_hash_table_del(chu->client->channels, chu->channel); + silc_hash_table_del(chu->channel->user_list, chu->client); + silc_client_unref_client(client, conn, chu->client); + silc_client_unref_channel(client, conn, chu->channel); + silc_free(chu); + } + silc_hash_table_list_reset(&htl); + + silc_hash_table_free(channel->user_list); +} diff --git a/lib/silcclient/client_channel.h b/lib/silcclient/client_channel.h index ac6b56a7..d72e2189 100644 --- a/lib/silcclient/client_channel.h +++ b/lib/silcclient/client_channel.h @@ -30,13 +30,20 @@ SilcBool silc_client_save_channel_key(SilcClient client, SilcChannelEntry channel); SilcChannelUser silc_client_on_channel(SilcChannelEntry channel, SilcClientEntry client_entry); -SilcBool silc_client_add_to_channel(SilcChannelEntry channel, +SilcBool silc_client_add_to_channel(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry channel, SilcClientEntry client_entry, SilcUInt32 cumode); -SilcBool silc_client_remove_from_channel(SilcChannelEntry channel, +SilcBool silc_client_remove_from_channel(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry channel, SilcClientEntry client_entry); void silc_client_remove_from_channels(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry); +void silc_client_empty_channel(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry channel); #endif /* CLIENT_CHANNEL_H */ diff --git a/lib/silcclient/client_connect.c b/lib/silcclient/client_connect.c index 3b656229..4aff0a6b 100644 --- a/lib/silcclient/client_connect.c +++ b/lib/silcclient/client_connect.c @@ -30,7 +30,6 @@ typedef struct { void *completion_context; } *VerifyKeyContext; - /************************ Static utility functions **************************/ /* Callback called after connected to remote host */ @@ -305,7 +304,8 @@ SILC_FSM_STATE(silc_client_st_connect) stream, fsm)); } else { /* Connect (TCP) */ - SILC_FSM_CALL(silc_net_tcp_connect(NULL, conn->remote_host, + SILC_FSM_CALL(conn->internal->op = silc_net_tcp_connect( + NULL, conn->remote_host, conn->remote_port, conn->internal->schedule, silc_client_connect_callback, fsm)); @@ -386,8 +386,9 @@ SILC_FSM_STATE(silc_client_st_connect_key_exchange) /** Run key exchange (TCP) */ silc_fsm_next(fsm, silc_client_st_connect_auth); - SILC_FSM_CALL(silc_ske_initiator(conn->internal->ske, conn->stream, - ¶ms, NULL)); + SILC_FSM_CALL(conn->internal->op = silc_ske_initiator(conn->internal->ske, + conn->stream, + ¶ms, NULL)); } /* For UDP/IP connections, set up the UDP session after successful key @@ -481,7 +482,8 @@ SILC_FSM_STATE(silc_client_st_connect_auth_start) /** Start connection authentication */ silc_fsm_next(fsm, silc_client_st_connected); - SILC_FSM_CALL(silc_connauth_initiator(connauth, SILC_CONN_CLIENT, + SILC_FSM_CALL(conn->internal->op = silc_connauth_initiator( + connauth, SILC_CONN_CLIENT, conn->internal->params.auth_method, conn->internal->params.auth, conn->internal->params.auth_len, @@ -526,9 +528,11 @@ SILC_FSM_STATE(silc_client_st_connected) SILC_FSM_STATE(silc_client_st_connect_error) { + SilcClientConnection conn = fsm_context; - /* XXX */ - /* Close connection */ + /* Signal to close connection */ + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); return SILC_FSM_FINISH; } diff --git a/lib/silcclient/client_entry.c b/lib/silcclient/client_entry.c index b7527433..43687fb0 100644 --- a/lib/silcclient/client_entry.c +++ b/lib/silcclient/client_entry.c @@ -22,8 +22,6 @@ #include "silcclient.h" #include "client_internal.h" -/* XXX locking */ - /************************ Client Searching Locally **************************/ /* Finds entry for client by the client's ID. Returns the entry or NULL @@ -108,7 +106,7 @@ SilcDList silc_client_get_clients_local(SilcClient client, /* Take all without any further checking */ silc_list_start(list); while ((id_cache = silc_list_get(list))) { - silc_client_ref_client(client, conn, entry); + silc_client_ref_client(client, conn, id_cache->context); silc_dlist_add(clients, id_cache->context); } } else { @@ -397,8 +395,11 @@ static SilcBool silc_client_get_clients_list_cb(SilcClient client, out: if (status != SILC_STATUS_OK && i->completion) i->completion(client, conn, status, NULL, i->context); + silc_client_list_free(client, conn, clients); + silc_buffer_free(i->client_id_list); silc_free(i); + return FALSE; } @@ -789,11 +790,12 @@ void silc_client_update_client(SilcClient client, /* Format nickname */ silc_client_nickname_format(client, conn, client_entry); - /* Remove the old cache entry and create a new one */ - silc_idcache_del_by_context(conn->internal->client_cache, client_entry, - NULL); - silc_idcache_add(conn->internal->client_cache, nick, &client_entry->id, - client_entry); + /* Update cache entry */ + silc_mutex_lock(conn->internal->lock); + silc_idcache_update_by_context(conn->internal->client_cache, + client_entry, NULL, nick, TRUE); + silc_mutex_unlock(conn->internal->lock); + client_entry->nickname_normalized = nick; } client_entry->mode = mode; } @@ -804,9 +806,9 @@ void silc_client_del_client_entry(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { - SILC_LOG_DEBUG(("Start")); - silc_free(client_entry->realname); + silc_free(client_entry->nickname_normalized); + silc_free(client_entry->internal.key); if (client_entry->public_key) silc_pkcs_public_key_free(client_entry->public_key); silc_hash_table_free(client_entry->channels); @@ -814,12 +816,16 @@ void silc_client_del_client_entry(SilcClient client, silc_cipher_free(client_entry->internal.send_key); if (client_entry->internal.receive_key) silc_cipher_free(client_entry->internal.receive_key); - silc_free(client_entry->internal.key); + if (client_entry->internal.hmac_send) + silc_hmac_free(client_entry->internal.hmac_send); + if (client_entry->internal.hmac_receive) + silc_hmac_free(client_entry->internal.hmac_receive); #if 0 silc_client_ftp_session_free_client(conn, client_entry); if (client_entry->internal->ke) silc_client_abort_key_agreement(client, conn, client_entry); #endif /* 0 */ + silc_atomic_uninit8(&client_entry->internal.refcnt); silc_free(client_entry); } @@ -828,9 +834,24 @@ void silc_client_del_client_entry(SilcClient client, SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry) { - SilcBool ret = silc_idcache_del_by_context(conn->internal->client_cache, - client_entry, NULL); -#if 0 + SilcBool ret; + + if (!client_entry) + return FALSE; + + SILC_LOG_DEBUG(("Client %p refcnt %d->%d", client_entry, + silc_atomic_get_int8(&client_entry->internal.refcnt), + silc_atomic_get_int8(&client_entry->internal.refcnt) - 1)); + if (silc_atomic_sub_int8(&client_entry->internal.refcnt, 1) > 0) + return FALSE; + + SILC_LOG_DEBUG(("Deleting client %p")); + + silc_mutex_lock(conn->internal->lock); + ret = silc_idcache_del_by_context(conn->internal->client_cache, + client_entry, NULL); + silc_mutex_unlock(conn->internal->lock); + if (ret) { /* Remove from channels */ silc_client_remove_from_channels(client, conn, client_entry); @@ -838,7 +859,6 @@ SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn, /* Free the client entry data */ silc_client_del_client_entry(client, conn, client_entry); } -#endif return ret; } @@ -884,7 +904,6 @@ void silc_client_list_free(SilcClient client, SilcClientConnection conn, } } - /* Formats the nickname of the client specified by the `client_entry'. If the format is specified by the application this will format the nickname and replace the old nickname in the client entry. If the @@ -1310,67 +1329,57 @@ SilcChannelEntry silc_client_add_channel(SilcClient client, return channel; } -/* Foreach callbcak to free all users from the channel when deleting a - channel entry. */ - -static void silc_client_del_channel_foreach(void *key, void *context, - void *user_context) -{ - SilcChannelUser chu = (SilcChannelUser)context; - - SILC_LOG_DEBUG(("Start")); - - /* Remove the context from the client's channel hash table as that - table and channel's user_list hash table share this same context. */ - silc_hash_table_del(chu->client->channels, chu->channel); - silc_free(chu); -} - /* Removes channel from the cache by the channel entry. */ SilcBool silc_client_del_channel(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel) { - SilcBool ret = silc_idcache_del_by_context(conn->internal->channel_cache, - channel, NULL); -#if 0 + SilcBool ret; + SilcCipher key; + SilcHmac hmac; - SILC_LOG_DEBUG(("Start")); + if (!channel) + return FALSE; + + if (silc_atomic_sub_int8(&channel->internal.refcnt, 1) > 0) + return FALSE; + + SILC_LOG_DEBUG(("Deleting channel %p", channel)); + + silc_mutex_lock(conn->internal->lock); + ret = silc_idcache_del_by_context(conn->internal->channel_cache, + channel, NULL); + silc_mutex_unlock(conn->internal->lock); - /* Free all client entrys from the users list. The silc_hash_table_free - will free all the entries so they are not freed at the foreach - callback. */ - silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach, - NULL); - silc_hash_table_free(channel->user_list); + if (!ret) + return FALSE; + silc_client_empty_channel(client, conn, channel); silc_free(channel->channel_name); silc_free(channel->topic); if (channel->founder_key) silc_pkcs_public_key_free(channel->founder_key); - silc_free(channel->key); - if (channel->channel_key) - silc_cipher_free(channel->channel_key); - if (channel->hmac) - silc_hmac_free(channel->hmac); - if (channel->old_channel_keys) { - SilcCipher key; - silc_dlist_start(channel->old_channel_keys); - while ((key = silc_dlist_get(channel->old_channel_keys)) != SILC_LIST_END) + if (channel->internal.channel_key) + silc_cipher_free(channel->internal.channel_key); + if (channel->internal.hmac) + silc_hmac_free(channel->internal.hmac); + if (channel->internal.old_channel_keys) { + silc_dlist_start(channel->internal.old_channel_keys); + while ((key = silc_dlist_get(channel->internal.old_channel_keys))) silc_cipher_free(key); - silc_dlist_uninit(channel->old_channel_keys); + silc_dlist_uninit(channel->internal.old_channel_keys); } - if (channel->old_hmacs) { - SilcHmac hmac; - silc_dlist_start(channel->old_hmacs); - while ((hmac = silc_dlist_get(channel->old_hmacs)) != SILC_LIST_END) + if (channel->internal.old_hmacs) { + silc_dlist_start(channel->internal.old_hmacs); + while ((hmac = silc_dlist_get(channel->internal.old_hmacs))) silc_hmac_free(hmac); - silc_dlist_uninit(channel->old_hmacs); + silc_dlist_uninit(channel->internal.old_hmacs); } - silc_schedule_task_del_by_context(conn->client->schedule, channel); silc_client_del_channel_private_keys(client, conn, channel); + silc_atomic_uninit8(&channel->internal.refcnt); + silc_schedule_task_del_by_context(conn->client->schedule, channel); silc_free(channel); -#endif + return ret; } @@ -1382,7 +1391,6 @@ SilcBool silc_client_replace_channel_id(SilcClient client, SilcChannelEntry channel, SilcChannelID *new_id) { - SilcIDCacheEntry id_cache; SilcBool ret = FALSE; if (!new_id) @@ -1393,14 +1401,10 @@ SilcBool silc_client_replace_channel_id(SilcClient client, SILC_LOG_DEBUG(("New Channel ID id(%s)", silc_id_render(new_id, SILC_ID_CHANNEL))); - silc_mutex_lock(conn->internal->lock); - /* Update the ID */ - if (silc_idcache_find_by_id_one(conn->internal->channel_cache, - &channel->id, &id_cache)) - ret = silc_idcache_update(conn->internal->channel_cache, id_cache, - &channel->id, new_id, NULL, NULL, FALSE); - + silc_mutex_lock(conn->internal->lock); + silc_idcache_update_by_context(conn->internal->channel_cache, channel, + new_id, NULL, FALSE); silc_mutex_unlock(conn->internal->lock); return ret; @@ -1468,6 +1472,7 @@ SilcServerEntry silc_client_get_server(SilcClient client, if (!silc_idcache_find_by_name_one(conn->internal->server_cache, server_name, &id_cache)) { silc_free(server_name); + silc_mutex_unlock(conn->internal->lock); return NULL; } @@ -1502,8 +1507,10 @@ SilcServerEntry silc_client_get_server_by_id(SilcClient client, silc_mutex_lock(conn->internal->lock); if (!silc_idcache_find_by_id_one(conn->internal->server_cache, - server_id, &id_cache)) + server_id, &id_cache)) { + silc_mutex_unlock(conn->internal->lock); return NULL; + } SILC_LOG_DEBUG(("Found")); @@ -1643,10 +1650,13 @@ SilcServerEntry silc_client_add_server(SilcClient client, SilcServerEntry server_entry; char *server_namec = NULL; - SILC_LOG_DEBUG(("Start")); + if (!server_id) + return NULL; + + SILC_LOG_DEBUG(("Adding new server %s", server_name)); server_entry = silc_calloc(1, sizeof(*server_entry)); - if (!server_entry || !server_id) + if (!server_entry) return NULL; silc_atomic_init8(&server_entry->internal.refcnt, 0); @@ -1692,11 +1702,28 @@ SilcServerEntry silc_client_add_server(SilcClient client, SilcBool silc_client_del_server(SilcClient client, SilcClientConnection conn, SilcServerEntry server) { - SilcBool ret = silc_idcache_del_by_context(conn->internal->server_cache, - server, NULL); + SilcBool ret; + + if (!server) + return FALSE; + + if (silc_atomic_sub_int8(&server->internal.refcnt, 1) > 0) + return FALSE; + + SILC_LOG_DEBUG(("Deleting server %p", server)); + + silc_mutex_lock(conn->internal->lock); + ret = silc_idcache_del_by_context(conn->internal->server_cache, + server, NULL); + silc_mutex_unlock(conn->internal->lock); + silc_free(server->server_name); silc_free(server->server_info); + if (server->public_key) + silc_pkcs_public_key_free(server->public_key); + silc_atomic_uninit8(&server->internal.refcnt); silc_free(server); + return ret; } @@ -1710,27 +1737,27 @@ void silc_client_update_server(SilcClient client, { char *server_namec = NULL; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Updating server %p", server_entry)); if (server_name && (!server_entry->server_name || !silc_utf8_strcasecmp(server_entry->server_name, server_name))) { - silc_idcache_del_by_context(conn->internal->server_cache, - server_entry, NULL); + server_namec = silc_identifier_check(server_name, strlen(server_name), + SILC_STRING_UTF8, 256, NULL); + if (!server_namec) + return; + silc_free(server_entry->server_name); server_entry->server_name = strdup(server_name); + if (!server_entry->server_name) + return; - /* Normalize server name */ - if (server_name) { - server_namec = silc_identifier_check(server_name, strlen(server_name), - SILC_STRING_UTF8, 256, NULL); - if (!server_namec) - return; - - silc_idcache_add(conn->internal->server_cache, server_namec, - &server_entry->id, server_entry); - } + /* Update cache entry */ + silc_mutex_lock(conn->internal->lock); + silc_idcache_update_by_context(conn->internal->server_cache, server_entry, + NULL, server_namec, TRUE); + silc_mutex_unlock(conn->internal->lock); } if (server_info && diff --git a/lib/silcclient/client_internal.h b/lib/silcclient/client_internal.h index 51c18227..3dbcd672 100644 --- a/lib/silcclient/client_internal.h +++ b/lib/silcclient/client_internal.h @@ -29,6 +29,18 @@ #include "client_channel.h" #include "client_notify.h" +/****************************** Definitions *********************************/ + +/* Packet retry counter and timer defines, for exponential backoff algorithm. + Meaningful with UDP transport when packets may get lost. */ +#define SILC_CLIENT_RETRY_COUNT 4 /* Max packet retry count */ +#define SILC_CLIENT_RETRY_MUL 2 /* Retry timer interval growth */ +#define SILC_CLIENT_RETRY_RAND 2 /* Randomizer, timeout += rnd % 2 */ +#define SILC_CLIENT_RETRY_MIN 1 /* Min retry timeout, seconds */ +#define SLIC_CLIENT_RETRY_MAX 16 /* Max retry timeout, seconds */ + +/********************************** Types ***********************************/ + /* Context to hold the connection authentication request callbacks that will be called when the server has replied back to our request about current authentication method in the session. */ @@ -123,15 +135,8 @@ struct SilcClientInternalStruct { SilcClientParams *params; /* Client parameters */ SilcPacketEngine packet_engine; /* Packet engine */ SilcMutex lock; /* Client lock */ - - /* List of connections in client. All the connection data is saved here. */ - SilcDList conns; - - /* Registered commands */ - SilcList commands; - - /* Client version. Used to compare to remote host's version strings. */ - char *silc_client_version; + SilcList commands; /* Registered commands */ + char *silc_client_version; /* Version set by application */ /* Events */ unsigned int run_callback : 1; /* Call running callback */ @@ -153,15 +158,17 @@ struct SilcClientConnectionInternalStruct { SilcList pending_commands; /* Pending commands list */ SilcHash hash; /* Negotiated hash function */ SilcHash sha1hash; /* SHA-1 default hash context */ + SilcBuffer local_idp; /* Local ID Payload */ + SilcBuffer remote_idp; /* Remote ID Payload */ + SilcAsyncOperation op; /* Protocols async operation */ SilcIDCache client_cache; /* Client entry cache */ SilcIDCache channel_cache; /* Channel entry cache */ SilcIDCache server_cache; /* Server entry cache */ - SilcBuffer local_idp; /* Local ID Payload */ - SilcBuffer remote_idp; /* Remote ID Payload */ - SilcAtomic16 cmd_ident; /* Current command identifier */ + SilcUInt8 retry_count; /* Packet retry counter */ + SilcUInt8 retry_timer; /* Packet retry timer */ /* Events */ unsigned int connect : 1; /* Connect remote host */ @@ -203,6 +210,7 @@ SilcUInt16 silc_client_command_send_argv(SilcClient client, unsigned char **argv, SilcUInt32 *argv_lens, SilcUInt32 *argv_types); +void silc_client_command_free(SilcClientCommandContext cmd); void silc_client_ftp(SilcClient client, SilcClientConnection conn, SilcPacket packet); diff --git a/lib/silcclient/client_keyagr.c b/lib/silcclient/client_keyagr.c index f408e492..a68d94a0 100644 --- a/lib/silcclient/client_keyagr.c +++ b/lib/silcclient/client_keyagr.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2001 - 2005 Pekka Riikonen + Copyright (C) 2001 - 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 @@ -16,35 +16,53 @@ GNU General Public License for more details. */ -/* $Id$ */ -/* This file includes the Key Agreement packet processing and actual - key agreement routines. This file has nothing to do with the actual - connection key exchange protocol, it is implemented in the client.c - and in protocol.c. This file implements the client-to-client key - agreement as defined by the SILC protocol. */ #include "silc.h" #include "silcclient.h" #include "client_internal.h" -SILC_TASK_CALLBACK(silc_client_key_agreement_final); -SILC_TASK_CALLBACK(silc_client_process_key_agreement); -SILC_TASK_CALLBACK(silc_client_key_agreement_timeout); -SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start); +/************************** Types and definitions ***************************/ /* Key agreement context */ struct SilcClientKeyAgreementStruct { - SilcClient client; - SilcClientConnection conn; - int fd; /* Listening/connection socket */ - SilcSocketConnection sock; /* Remote socket connection */ - SilcClientEntry client_entry; /* Destination client */ + SilcClient client; /* Client */ + SilcClientConnection conn; /* Server connection */ SilcKeyAgreementCallback completion; /* Key agreement completion */ void *context; /* User context */ - SilcTask timeout; /* Timeout task */ - SilcClientKEInternalContext *proto_ctx; /* Key Exchange protocol context */ + + /* Responder */ + SilcNetListener listener; /* TCP listener */ + SilcStream stream; /* Remote connection (TCP or UDP) */ + + /* Initiator */ + SilcClientConnection client_conn; /* Connection to remote client */ }; +/************************ Static utility functions **************************/ + +/* TCP network listener callback. Accepts new key agreement connection */ + +static void silc_client_tcp_accept(SilcNetStatus status, + SilcStream stream, + void *context) +{ + SilcClientEntry client_entry = context; + SilcClientKeyAgreement ke = client_entry->ke; + + ke->stream = stream; + silc_client_process_key_agreement(ke->client, ke->conn, ke); +} + +/* UDP network callback. All UDP packets are read from here. */ + +static void silc_client_udp_accept(SilcStream stream, + SilcStreamStatus status, + void *context) +{ + +} + + /* Packet sending function used by the SKE in the key agreement process. */ static void silc_client_key_agreement_send_packet(SilcSKE ske, @@ -254,62 +272,32 @@ SILC_TASK_CALLBACK(silc_client_key_agreement_timeout) silc_free(ke); } -/* Sends key agreement request to the remote client indicated by the - `client_entry'. If the caller provides the `hostname' and the `port' - arguments then the library will bind the client to that hostname and - that port for the key agreement protocol. It also sends the `hostname' - and the `port' in the key agreement packet to the remote client. This - would indicate that the remote client may initiate the key agreement - protocol to the `hostname' on the `port'. If port is zero then the - bound port is undefined (the operating system defines it). - - If the `hostname' and `port' is not provided then empty key agreement - packet is sent to the remote client. The remote client may reply with - the same packet including its hostname and port. If the library receives - the reply from the remote client the `key_agreement' client operation - callback will be called to verify whether the user wants to perform the - key agreement or not. - - NOTE: If the application provided the `hostname' and the `port' and the - remote side initiates the key agreement protocol it is not verified - from the user anymore whether the protocol should be executed or not. - By setting the `hostname' and `port' the user gives permission to - perform the protocol (we are responder in this case). - - NOTE: If the remote side decides not to initiate the key agreement - or decides not to reply with the key agreement packet then we cannot - perform the key agreement at all. If the key agreement protocol is - performed the `completion' callback with the `context' will be called. - If remote side decides to ignore the request the `completion' will be - called after the specified timeout, `timeout_secs'. - - NOTE: If the `hostname' and the `port' was not provided the `completion' - will not be called at all since this does nothing more than sending - a packet to the remote host. - - NOTE: There can be only one active key agreement for one client entry. - Before setting new one, the old one must be finished (it is finished - after calling the completion callback) or the function - silc_client_abort_key_agreement must be called. */ +/*************************** Key Agreement API ******************************/ + +/* Sends key agreement packet to remote client. If IP addresses are provided + creates also listener for íncoming key agreement connection. Supports + both TCP and UDP transports. */ void silc_client_send_key_agreement(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry, - const char *hostname, - const char *bindhost, + const char *local_ip, + const char *bind_ip, int port, SilcUInt32 timeout_secs, + SilcBool udp, SilcKeyAgreementCallback completion, void *context) { - SilcSocketConnection sock = conn->sock; SilcClientKeyAgreement ke = NULL; + SilcAsyncOperation op; SilcBuffer buffer; + SilcUInt16 ports = NULL; if (!client_entry) return; - if (client_entry->ke) { + if (client_entry->internal->ke) { completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ALREADY_STARTED, NULL, context); return; @@ -321,111 +309,91 @@ void silc_client_send_key_agreement(SilcClient client, return; } - /* Create the listener if hostname and port was provided. - * also, use bindhost if it was specified. - */ - - if (hostname) { + /* If local IP is provided, create listener */ + if (local_ip || bind_ip) { ke = silc_calloc(1, sizeof(*ke)); + if (!ke) { + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY, + NULL, context); + return; + } - if (bindhost) - ke->fd = silc_net_create_server(port, bindhost); - else - ke->fd = silc_net_create_server(port, hostname); - - if (ke->fd < 0) { - client->internal->ops->say( + /* Create network listener */ + if (udp) { + /* UDP listener */ + ke->stream = + silc_net_udp_connect(bind_ip ? bind_ip : local_ip, port, NULL, 0, + client_entry); + if (!ke->stream) { + client->internal->ops->say( + client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot create UDP listener on %s on port %d: %s", + bind_ip ? bind_ip : local_ip, port, strerror(errno)); + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR, + NULL, context); + silc_free(ke); + return; + } + silc_stream_set_notifier(ke->stream, conn->schedule, + silc_client_udp_accept, client_entry); + } else { + /* TCP listener */ + ke->listener = + silc_net_tcp_create_listener(bind_ip ? &bind_ip : + &local_ip, 1, port, FALSE, + FALSE, conn->internal->schedule, + silc_client_tcp_accept, + client_entry); + if (!ke->listener) { + client->internal->ops->say( client, conn, SILC_CLIENT_MESSAGE_ERROR, "Cannot create listener on %s on port %d: %s", - (bindhost) ? bindhost:hostname, port, strerror(errno)); - completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE, - NULL, context); - silc_free(ke); - return; + bind_ip ? bind_ip : local_ip, port, strerror(errno)); + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_ERROR, + NULL, context); + silc_free(ke); + return; + } } ke->client = client; ke->conn = conn; - ke->client_entry = client_entry; ke->completion = completion; ke->context = context; - - /* Add listener task to the scheduler. This task receives the key - negotiations. */ - silc_schedule_task_add(client->schedule, ke->fd, - silc_client_process_key_agreement, - (void *)ke, 0, 0, - SILC_TASK_FD, - SILC_TASK_PRI_NORMAL); - - /* Register a timeout task that will be executed if the connector - will not start the key exchange protocol within the specified - timeout. */ - ke->timeout = silc_schedule_task_add(client->schedule, 0, - silc_client_key_agreement_timeout, - (void *)ke, timeout_secs, 0, - SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); } - /* Encode the key agreement payload */ - buffer = silc_key_agreement_payload_encode(hostname, - !ke ? port : - silc_net_get_local_port(ke->fd)); - - /* Send the key agreement packet to the client */ - silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT, - client_entry->id, SILC_ID_CLIENT, NULL, NULL, - buffer->data, buffer->len, FALSE); - silc_buffer_free(buffer); -} - -static int -silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx) -{ - int sock; - - /* Create connection to server asynchronously */ - sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host); - if (sock < 0) - return -1; + /* Add key agreement timeout task */ + silc_schedule_task_add_timeout(conn->internal->schedule, + silc_client_key_agreement_timeout, + client_entry, timeout_secs, 0); - /* 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_perform_key_agreement_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; + /* Encode the key agreement payload */ + if (ke && ke->listener) + ports = silc_net_listener_get_port(ke->listener, NULL); + buffer = silc_key_agreement_payload_encode(local_ip, (port ? port : + ports ? ports[0] : 0)); + if (!buffer) { + if (ke) { + if (ke->listener) + silc_net_close_listener(ke->listener); + silc_free(ke); + } + completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY, + NULL, context); + return; + } + silc_free(ports); - return sock; -} + if (ke) { + silc_client_ref_client(client, conn, client_entry); + client_entry->internal.ke = ke; + } -/* Routine used by silc_client_perform_key_agreement to create connection - to the remote client on specified port. */ + /* Send the key agreement packet to the client */ + silc_packet_send(conn->stream, SILC_PACKET_KEY_AGREEMENT, 0, + silc_buffer_data(buffer), silc_buffer_len(data)); -static int -silc_client_connect_to_client(SilcClient client, - SilcClientConnection conn, int port, - char *host, void *context) -{ - SilcClientInternalConnectContext *ctx; - - /* 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; - ctx->tries = 0; - ctx->context = context; - - /* Do the actual connecting process */ - return silc_client_connect_to_client_internal(ctx); + silc_buffer_free(buffer); } /* Callback that is called after connection has been created. This actually diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index 206425dc..e498e9c6 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -238,6 +238,8 @@ SILC_FSM_STATE(silc_client_notify_none) SilcNotifyType type = silc_notify_get_type(payload); SilcArgumentPayload args = silc_notify_get_args(payload); + SILC_LOG_DEBUG(("Notify: NONE")); + /* Notify application */ NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL)); @@ -380,7 +382,7 @@ SILC_FSM_STATE(silc_client_notify_join) silc_client_nickname_format(client, conn, client_entry); /* Join the client to channel */ - if (!silc_client_add_to_channel(channel, client_entry, 0)) + if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0)) goto out; /* Notify application. */ @@ -410,7 +412,6 @@ SILC_FSM_STATE(silc_client_notify_leave) SilcArgumentPayload args = silc_notify_get_args(payload); SilcClientEntry client_entry = NULL; SilcChannelEntry channel = NULL; - SilcChannelUser chu; SilcID id; SILC_LOG_DEBUG(("Notify: LEAVE")); @@ -444,30 +445,7 @@ SILC_FSM_STATE(silc_client_notify_leave) goto out; /* Remove client from channel */ - chu = silc_client_on_channel(channel, client_entry); - if (chu) { - silc_hash_table_del(client_entry->channels, channel); - silc_hash_table_del(channel->user_list, client_entry); - silc_free(chu); - } - -#if 0 /* Kind of useless, server will return error if client keeps using - non-existing client, and the entry is removed then. */ - /* Some client implementations actually quit network by first doing - LEAVE and then immediately SIGNOFF. We'll check for this by doing - check for the client after 5 - 34 seconds. If it is not valid after - that we'll remove the client from cache. */ - if (!silc_hash_table_count(client_entry->channels)) { - SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res)); - res->context = client; - res->sock = silc_socket_dup(conn->sock); - res->packet = silc_id_dup(client_id, SILC_ID_CLIENT); - silc_schedule_task_add(client->schedule, conn->sock->sock, - silc_client_notify_check_client, res, - (5 + (silc_rng_get_rn16(client->rng) % 29)), - 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - } -#endif + silc_client_remove_from_channel(client, conn, channel, client_entry); /* Notify application. */ NOTIFY(client, conn, type, client_entry, channel); @@ -713,9 +691,10 @@ SILC_FSM_STATE(silc_client_notify_nick_change) silc_utf8_strcasecmp(tmp, client_entry->nickname)) { /* Nickname didn't change. Update only Client ID. We don't notify application because nickname didn't change. */ - silc_idcache_update(conn->internal->client_cache, client_entry, - &client_entry->id, &id2.u.client_id, NULL, - NULL, FALSE); + silc_mutex_lock(conn->internal->lock); + silc_idcache_update_by_context(conn->internal->client_cache, client_entry, + &id2.u.client_id, NULL, FALSE); + silc_mutex_unlock(conn->internal->lock); goto out; } @@ -725,12 +704,14 @@ SILC_FSM_STATE(silc_client_notify_nick_change) goto out; /* Update nickname */ - if (!silc_idcache_update(conn->internal->client_cache, client_entry, - NULL, NULL, client_entry->nickname_normalized, - nick, TRUE)) { + silc_mutex_lock(conn->internal->lock); + if (!silc_idcache_update_by_context(conn->internal->client_cache, + client_entry, NULL, nick, TRUE)) { silc_free(nick); + silc_mutex_unlock(conn->internal->lock); goto out; } + silc_mutex_unlock(conn->internal->lock); memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname)); memcpy(client_entry->nickname, tmp, tmp_len); client_entry->nickname_normalized = nick; @@ -967,6 +948,22 @@ SILC_FSM_STATE(silc_client_notify_cumode_change) /* NOT REACHED */ } + /* Get target Client ID */ + if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL)) + goto out; + + /* Find target Client entry */ + client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id); + if (!client_entry2 || !client_entry2->nickname[0]) { + /** Resolve client */ + silc_client_unref_client(client, conn, client_entry2); + SILC_FSM_CALL(silc_client_get_client_by_id_resolve( + client, conn, &id2.u.client_id, NULL, + silc_client_notify_resolved, + notify)); + /* NOT REACHED */ + } + /* Get the mode */ tmp = silc_argument_get_arg_type(args, 2, &tmp_len); if (!tmp) @@ -1023,23 +1020,6 @@ SILC_FSM_STATE(silc_client_notify_cumode_change) entry = channel_entry; } - /* Get target Client ID */ - if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL)) - goto out; - - /* Find target Client entry */ - client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id); - if (!client_entry2 || !client_entry2->nickname[0]) { - /** Resolve client */ - silc_client_unref_client(client, conn, client_entry); - silc_client_unref_client(client, conn, client_entry2); - SILC_FSM_CALL(silc_client_get_client_by_id_resolve( - client, conn, &id2.u.client_id, NULL, - silc_client_notify_resolved, - notify)); - /* NOT REACHED */ - } - /* Save the mode */ chu = silc_client_on_channel(channel, client_entry2); if (chu) diff --git a/lib/silcclient/client_prvmsg.c b/lib/silcclient/client_prvmsg.c index 61c43b62..a4addb35 100644 --- a/lib/silcclient/client_prvmsg.c +++ b/lib/silcclient/client_prvmsg.c @@ -669,12 +669,17 @@ silc_client_list_private_message_keys(SilcClient client, if (!client || !conn) return NULL; - if (!silc_idcache_get_all(conn->internal->client_cache, &list)) + silc_mutex_lock(conn->internal->lock); + if (!silc_idcache_get_all(conn->internal->client_cache, &list)) { + silc_mutex_unlock(conn->internal->lock); return NULL; + } keys = silc_calloc(silc_list_count(list), sizeof(*keys)); - if (!keys) + if (!keys) { + silc_mutex_unlock(conn->internal->lock); return NULL; + } silc_list_start(list); while ((id_cache = silc_list_get(list))) { @@ -691,6 +696,8 @@ silc_client_list_private_message_keys(SilcClient client, } } + silc_mutex_unlock(conn->internal->lock); + if (key_count) *key_count = count; diff --git a/lib/silcclient/client_register.c b/lib/silcclient/client_register.c index 8ff3fb4c..84f35f72 100644 --- a/lib/silcclient/client_register.c +++ b/lib/silcclient/client_register.c @@ -36,9 +36,21 @@ typedef struct { SilcBool success; } *SilcClientResumeSession; - /************************ Static utility functions **************************/ +/* Command callback. Nothing interesting to do here. */ + +static SilcBool +silc_client_register_command_called(SilcClient client, + SilcClientConnection conn, + SilcCommand command, + SilcStatus status, + SilcStatus error, + void *context, + va_list ap) +{ + return TRUE; +} /****************************** NEW_ID packet *******************************/ @@ -77,10 +89,14 @@ SILC_FSM_STATE(silc_client_new_id) conn->internal->local_idp = silc_buffer_copy(&packet->buffer); /* Save cache entry */ + silc_mutex_lock(conn->internal->lock); if (!silc_idcache_find_by_id_one(conn->internal->client_cache, conn->local_id, - &conn->internal->local_entry)) + &conn->internal->local_entry)) { + silc_mutex_unlock(conn->internal->lock); goto out; + } + silc_mutex_unlock(conn->internal->lock); /* Save remote ID */ if (packet->src_id_len) { @@ -138,7 +154,8 @@ SILC_FSM_STATE(silc_client_st_register) /** Wait for new ID */ conn->internal->registering = TRUE; - silc_fsm_next_later(fsm, silc_client_st_register_complete, 15, 0); + silc_fsm_next_later(fsm, silc_client_st_register_complete, + conn->internal->retry_timer, 0); return SILC_FSM_WAIT; } @@ -150,9 +167,21 @@ SILC_FSM_STATE(silc_client_st_register_complete) SilcClient client = conn->client; if (!conn->local_id) { - /** Timeout, ID not received */ - conn->internal->registering = FALSE; - silc_fsm_next(fsm, silc_client_st_register_error); + if (conn->internal->retry_count++ >= SILC_CLIENT_RETRY_COUNT) { + /** Timeout, ID not received */ + conn->internal->registering = FALSE; + conn->internal->retry_count = 0; + conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN; + silc_fsm_next(fsm, silc_client_st_register_error); + return SILC_FSM_CONTINUE; + } + + /** Resend registering packet */ + silc_fsm_next(fsm, silc_client_st_register); + conn->internal->retry_timer = ((conn->internal->retry_timer * + SILC_CLIENT_RETRY_MUL) + + (silc_rng_get_rn16(client->rng) % + SILC_CLIENT_RETRY_RAND)); return SILC_FSM_CONTINUE; } @@ -160,21 +189,22 @@ SILC_FSM_STATE(silc_client_st_register_complete) /* Issue IDENTIFY command for itself to get resolved hostname correctly from server. */ - silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, NULL, NULL, + silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, + silc_client_register_command_called, NULL, 1, 5, silc_buffer_data(conn->internal->local_idp), silc_buffer_len(conn->internal->local_idp)); - /* Send NICK command if the nickname was set by the application (and is - not same as the username). Send this with little timeout. */ + /* Call NICK command if the nickname was set by the application (and is + not same as the username). */ if (conn->internal->params.nickname && !silc_utf8_strcasecmp(conn->internal->params.nickname, client->username)) - silc_client_command_send(client, conn, SILC_COMMAND_NICK, NULL, NULL, - 1, 1, conn->internal->params.nickname, - strlen(conn->internal->params.nickname)); + silc_client_command_call(client, conn, NULL, + "NICK", conn->internal->params.nickname, NULL); /* Issue INFO command to fetch the real server name and server information and other stuff. */ - silc_client_command_send(client, conn, SILC_COMMAND_INFO, NULL, NULL, + silc_client_command_send(client, conn, SILC_COMMAND_INFO, + silc_client_register_command_called, NULL, 1, 2, silc_buffer_data(conn->internal->remote_idp), silc_buffer_len(conn->internal->remote_idp)); @@ -186,14 +216,20 @@ SILC_FSM_STATE(silc_client_st_register_complete) return SILC_FSM_FINISH; } +/* Error registering to network */ + SILC_FSM_STATE(silc_client_st_register_error) { SilcClientConnection conn = fsm_context; SilcClient client = conn->client; - /* XXX */ - /* Close connection */ + SILC_LOG_DEBUG(("Error registering to network")); + + /* Signal to close connection */ + conn->internal->disconnected = TRUE; + SILC_FSM_SEMA_POST(&conn->internal->wait_event); + /* Call connect callback */ conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL, conn->callback_context); diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 4accee1a..468ac1bb 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -139,11 +139,17 @@ silc_client_command_register(SilcClient client, SilcClientCommand cmd; cmd = silc_calloc(1, sizeof(*cmd)); + if (!cmd) + return FALSE; cmd->cmd = command; cmd->command = command_func; cmd->reply = command_reply_func; - cmd->name = name ? strdup(name) : NULL; cmd->max_args = max_args; + cmd->name = name ? strdup(name) : NULL; + if (!cmd->name) { + silc_free(cmd); + return FALSE; + } silc_list_add(client->internal->commands, cmd); @@ -191,20 +197,6 @@ static SilcClientCommand silc_client_command_find(SilcClient client, return NULL; } -/* Free command context and its internals */ - -static void silc_client_command_free(SilcClientCommandContext cmd) -{ - int i; - - for (i = 0; i < cmd->argc; i++) - silc_free(cmd->argv[i]); - silc_free(cmd->argv); - silc_free(cmd->argv_lens); - silc_free(cmd->argv_types); - silc_free(cmd); -} - /* Command thread destructor */ static void silc_client_command_destructor(SilcFSMThread thread, @@ -353,6 +345,26 @@ static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn, /****************************** Command API *********************************/ +/* Free command context and its internals */ + +void silc_client_command_free(SilcClientCommandContext cmd) +{ + SilcClientCommandReplyCallback cb; + int i; + + for (i = 0; i < cmd->argc; i++) + silc_free(cmd->argv[i]); + silc_free(cmd->argv); + silc_free(cmd->argv_lens); + silc_free(cmd->argv_types); + + silc_list_start(cmd->reply_callbacks); + while ((cb = silc_list_get(cmd->reply_callbacks))) + silc_free(cb); + + silc_free(cmd); +} + /* Executes a command */ SilcUInt16 silc_client_command_call(SilcClient client, @@ -435,6 +447,8 @@ SilcUInt16 silc_client_command_call(SilcClient client, cmd->cmd_ident = silc_client_cmd_ident(conn); cmd->called = TRUE; cmd->verbose = TRUE; + silc_list_init(cmd->reply_callbacks, + struct SilcClientCommandReplyCallbackStruct, next); /*** Call command */ SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd))); @@ -467,6 +481,8 @@ SilcUInt16 silc_client_command_send(SilcClient client, return 0; cmd->conn = conn; cmd->cmd = command; + silc_list_init(cmd->reply_callbacks, + struct SilcClientCommandReplyCallbackStruct, next); /* Send the command */ va_start(ap, argc); @@ -2577,6 +2593,7 @@ SILC_FSM_STATE(silc_client_command_getkey) /* NOT REACHED */ } idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER); + silc_client_unref_server(client, conn, server_entry); } else { client_entry = silc_dlist_get(clients); idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT); diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index a4d51b0d..f5adc9c2 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -188,9 +188,13 @@ SILC_FSM_STATE(silc_client_command_reply_wait) SILC_FSM_STATE(silc_client_command_reply_timeout) { SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; SilcArgumentPayload args = NULL; + SILC_LOG_DEBUG(("Command %s timeout", silc_get_command_name(cmd->cmd))); + /* Timeout, reply not received in timely fashion */ + silc_list_del(conn->internal->pending_commands, cmd); ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT); return SILC_FSM_FINISH; } @@ -680,10 +684,7 @@ SILC_FSM_STATE(silc_client_command_reply_nick) silc_mutex_lock(conn->internal->lock); if (!silc_idcache_update(conn->internal->client_cache, conn->internal->local_entry, - &conn->local_entry->id, - &id.u.client_id, - conn->local_entry->nickname_normalized, - tmp, TRUE)) { + &id.u.client_id, tmp, TRUE)) { silc_free(tmp); silc_mutex_unlock(conn->internal->lock); ERROR_CALLBACK(SILC_STATUS_ERR_BAD_NICKNAME); @@ -950,16 +951,18 @@ SILC_FSM_STATE(silc_client_command_reply_info) /* See whether we have this server cached. If not create it. */ server = silc_client_get_server_by_id(client, conn, &id.u.server_id); if (!server) { - SILC_LOG_DEBUG(("New server entry")); + SILC_LOG_DEBUG(("Add new server entry (INFO)")); server = silc_client_add_server(client, conn, server_name, server_info, &id.u.server_id); if (!server) goto out; + silc_client_ref_server(client, conn, server); } /* Notify application */ silc_client_command_callback(cmd, server, server->server_name, server->server_info); + silc_client_unref_server(client, conn, server); out: silc_fsm_next(fsm, silc_client_command_reply_processed); @@ -1171,15 +1174,13 @@ SILC_FSM_STATE(silc_client_command_reply_join) /* Mode */ SILC_GET32_MSB(mode, client_mode_list.data); - SILC_LOG_DEBUG(("id %s", silc_id_render(&id.u.client_id, SILC_ID_CLIENT))); - /* Get client entry */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); if (!client_entry) continue; /* Join client to the channel */ - silc_client_add_to_channel(channel, client_entry, mode); + silc_client_add_to_channel(client, conn, channel, client_entry, mode); silc_client_unref_client(client, conn, client_entry); if (!silc_buffer_pull(&client_id_list, idp_len)) @@ -1724,7 +1725,7 @@ SILC_FSM_STATE(silc_client_command_reply_leave) } /* Remove us from this channel. */ - silc_client_remove_from_channel(channel, conn->local_entry); + silc_client_remove_from_channel(client, conn, channel, conn->local_entry); /* Notify application */ silc_client_command_callback(cmd, channel); @@ -1865,7 +1866,7 @@ SILC_FSM_STATE(silc_client_command_reply_users) clearly do not exist since the resolving didn't find them. */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); if (client_entry) - silc_client_add_to_channel(channel, client_entry, mode); + silc_client_add_to_channel(client, conn, channel, client_entry, mode); silc_client_unref_client(client, conn, client_entry); if (!silc_buffer_pull(&client_id_list, idp_len)) @@ -1961,6 +1962,7 @@ SILC_FSM_STATE(silc_client_command_reply_getkey) /* Notify application */ silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry, server_entry->public_key); + silc_client_unref_server(client, conn, server_entry); } out: diff --git a/lib/silcclient/silcclient.h b/lib/silcclient/silcclient.h index bb7bc9aa..b44b09ff 100644 --- a/lib/silcclient/silcclient.h +++ b/lib/silcclient/silcclient.h @@ -269,6 +269,7 @@ typedef enum { SILC_KEY_AGREEMENT_ABORTED, /* The protocol aborted */ SILC_KEY_AGREEMENT_ALREADY_STARTED, /* Already started */ SILC_KEY_AGREEMENT_SELF_DENIED, /* Negotiationg with itself denied */ + SILC_KEY_AGREEMENT_NO_MEMORY, /* System out of memory */ } SilcKeyAgreementStatus; /***/ @@ -643,11 +644,6 @@ typedef struct { in the callbacks to protect application specific data. */ SilcBool threads; - /* Number of maximum tasks the client library's scheduler can handle. - If set to zero default value will be used. For WIN32 systems this - should be set to 64 as it is the hard limit dictated by the WIN32. */ - int task_max; - /* Rekey timeout in seconds. The client will perform rekey in this time interval. If set to zero, the default value will be used. */ unsigned int rekey_secs; -- 2.24.0