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
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);
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);
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);
}
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;
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,
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
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;
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
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),
/* 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;
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 */
/* 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)
{
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);
/* 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;
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;
}
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);
+}
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 */
void *completion_context;
} *VerifyKeyContext;
-
/************************ Static utility functions **************************/
/* Callback called after connected to remote host */
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));
/** 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
/** 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,
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;
}
#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
/* 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 {
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;
}
/* 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;
}
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);
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);
}
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);
/* Free the client entry data */
silc_client_del_client_entry(client, conn, client_entry);
}
-#endif
return ret;
}
}
}
-
/* 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
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;
}
SilcChannelEntry channel,
SilcChannelID *new_id)
{
- SilcIDCacheEntry id_cache;
SilcBool ret = FALSE;
if (!new_id)
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;
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;
}
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"));
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);
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;
}
{
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 &&
#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. */
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 */
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 */
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);
Author: Pekka Riikonen <priikone@silcnet.org>
- 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
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,
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;
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
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));
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. */
SilcArgumentPayload args = silc_notify_get_args(payload);
SilcClientEntry client_entry = NULL;
SilcChannelEntry channel = NULL;
- SilcChannelUser chu;
SilcID id;
SILC_LOG_DEBUG(("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);
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;
}
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;
/* 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)
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)
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))) {
}
}
+ silc_mutex_unlock(conn->internal->lock);
+
if (key_count)
*key_count = count;
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 *******************************/
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) {
/** 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;
}
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;
}
/* 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));
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);
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);
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,
/****************************** 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,
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)));
return 0;
cmd->conn = conn;
cmd->cmd = command;
+ silc_list_init(cmd->reply_callbacks,
+ struct SilcClientCommandReplyCallbackStruct, next);
/* Send the command */
va_start(ap, argc);
/* 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);
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;
}
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);
/* 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);
/* 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))
}
/* 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);
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))
/* 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:
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;
/***/
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;