From 2899e1a5409cea132653cd67d4e33e9313872e95 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Fri, 2 Feb 2007 20:32:29 +0000 Subject: [PATCH] Fixed entry resolving while processing incoming notify packets, when the IDs in the notify may become invalid while resolving (like when remote changes nickname at the same time). Entry is also now invalid if it doesn't have nickname. Serialized JOIN and NICK commands to avoid problems locally with changing ID while joining channel. --- lib/silcclient/client.h | 4 +- lib/silcclient/client_channel.c | 37 ++++-- lib/silcclient/client_connect.c | 12 +- lib/silcclient/client_entry.c | 51 ++++++++- lib/silcclient/client_entry.h | 6 +- lib/silcclient/client_internal.h | 1 + lib/silcclient/client_keyagr.c | 2 +- lib/silcclient/client_notify.c | 181 +++++++++++++++++++----------- lib/silcclient/client_prvmsg.c | 17 ++- lib/silcclient/command.c | 30 ++++- lib/silcclient/command_reply.c | 4 +- lib/silcclient/silcclient_entry.h | 41 +++++-- 12 files changed, 281 insertions(+), 105 deletions(-) diff --git a/lib/silcclient/client.h b/lib/silcclient/client.h index dd4eb8a9..d84cc162 100644 --- a/lib/silcclient/client.h +++ b/lib/silcclient/client.h @@ -54,8 +54,8 @@ typedef struct SilcClientEntryInternalStruct { SilcClientKeyAgreement ke; /* Current key agreement context or NULL */ /* Flags */ - unsigned int valid : 1; /* FALSE if this entry is not valid */ - unsigned int resolving : 1; /* TRUE when entry is being resolved */ + unsigned int valid : 1; /* FALSE if this entry is not valid. Entry + without nickname is not valid. */ unsigned int generated : 1; /* TRUE if library generated `key' */ unsigned int prv_resp : 1; /* TRUE if we are responder when using private message keys. */ diff --git a/lib/silcclient/client_channel.c b/lib/silcclient/client_channel.c index e8cfa7d1..8f8772e1 100644 --- a/lib/silcclient/client_channel.c +++ b/lib/silcclient/client_channel.c @@ -40,6 +40,7 @@ SilcBool silc_client_send_channel_message(SilcClient client, SilcCipher cipher; SilcHmac hmac; SilcBool ret; + SilcID sid, rid; SILC_LOG_DEBUG(("Sending channel message")); @@ -109,9 +110,14 @@ SilcBool silc_client_send_channel_message(SilcClient client, } /* Encode the message payload. This also encrypts the message payload. */ + sid.type = SILC_ID_CLIENT; + sid.u.client_id = chu->client->id; + rid.type = SILC_ID_CHANNEL; + rid.u.channel_id = chu->channel->id; buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE, cipher, hmac, client->rng, NULL, - conn->private_key, hash, NULL); + conn->private_key, hash, &sid, &rid, + NULL); if (silc_unlikely(!buffer)) { SILC_LOG_ERROR(("Error encoding channel message")); return FALSE; @@ -182,7 +188,7 @@ SILC_FSM_STATE(silc_client_channel_message) /* Get sender client entry */ client_entry = silc_client_get_client_by_id(client, conn, &remote_id); - if (!client_entry || !client_entry->nickname[0]) { + if (!client_entry || !client_entry->internal.valid) { /** Resolve client info */ silc_client_unref_client(client, conn, client_entry); SILC_FSM_CALL(silc_client_get_client_by_id_resolve( @@ -225,8 +231,10 @@ SILC_FSM_STATE(silc_client_channel_message) payload = silc_message_payload_parse(silc_buffer_data(buffer), silc_buffer_len(buffer), FALSE, FALSE, channel->internal.receive_key, - channel->internal.hmac, NULL, - FALSE, NULL); + channel->internal.hmac, + packet->src_id, packet->src_id_len, + packet->dst_id, packet->dst_id_len, + NULL, FALSE, NULL); /* If decryption failed and we have just performed channel key rekey we will use the old key in decryption. If that fails too then we @@ -251,6 +259,10 @@ SILC_FSM_STATE(silc_client_channel_message) payload = silc_message_payload_parse(silc_buffer_data(buffer), silc_buffer_len(buffer), FALSE, FALSE, cipher, hmac, + packet->src_id, + packet->src_id_len, + packet->dst_id, + packet->dst_id_len, NULL, FALSE, NULL); if (payload) break; @@ -266,8 +278,12 @@ SILC_FSM_STATE(silc_client_channel_message) silc_buffer_len(buffer), FALSE, FALSE, channel->internal.receive_key, - channel->internal.hmac, NULL, - FALSE, NULL); + channel->internal.hmac, + packet->src_id, + packet->src_id_len, + packet->dst_id, + packet->dst_id_len, + NULL, FALSE, NULL); if (!payload) { silc_dlist_start(channel->internal.private_keys); @@ -276,7 +292,11 @@ SILC_FSM_STATE(silc_client_channel_message) payload = silc_message_payload_parse(silc_buffer_data(buffer), silc_buffer_len(buffer), FALSE, FALSE, key->cipher, - key->hmac, NULL, FALSE, NULL); + key->hmac, packet->src_id, + packet->src_id_len, + packet->dst_id, + packet->dst_id_len, + NULL, FALSE, NULL); if (payload) break; } @@ -735,7 +755,8 @@ SilcBool silc_client_add_to_channel(SilcClient client, return TRUE; } -/* Removes client from a channel. This handles entry locking internally. */ +/* Removes client from a channel. Returns FALSE if user is not on channel. + This handles entry locking internally. */ SilcBool silc_client_remove_from_channel(SilcClient client, SilcClientConnection conn, diff --git a/lib/silcclient/client_connect.c b/lib/silcclient/client_connect.c index c40856a3..ec7e81e5 100644 --- a/lib/silcclient/client_connect.c +++ b/lib/silcclient/client_connect.c @@ -658,6 +658,10 @@ SILC_FSM_STATE(silc_client_st_connected) SilcClientConnection conn = fsm_context; SilcClient client = conn->client; + /* Get SILC protocol version remote supports */ + silc_ske_parse_version(conn->internal->ske, &conn->internal->remote_version, + NULL, NULL, NULL, NULL); + silc_ske_free(conn->internal->ske); conn->internal->ske = NULL; @@ -733,9 +737,11 @@ SILC_TASK_CALLBACK(silc_client_rekey_timer) SilcClientConnection conn = context; /* Signal to start rekey */ - conn->internal->rekey_responder = FALSE; - conn->internal->rekeying = TRUE; - SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event); + if (!silc_fsm_is_started(&conn->internal->event_thread)) { + conn->internal->rekey_responder = FALSE; + conn->internal->rekeying = TRUE; + SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event); + } /* Reinstall rekey timer */ silc_schedule_task_add_timeout(conn->internal->schedule, diff --git a/lib/silcclient/client_entry.c b/lib/silcclient/client_entry.c index 706a4afe..1cc18337 100644 --- a/lib/silcclient/client_entry.c +++ b/lib/silcclient/client_entry.c @@ -136,6 +136,7 @@ typedef struct { SilcDList clients; SilcGetClientCallback completion; void *context; + SilcClientEntry client_entry; } *SilcClientGetClientInternal; /* Resolving command callback */ @@ -153,6 +154,12 @@ static SilcBool silc_client_get_clients_cb(SilcClient client, if (error != SILC_STATUS_OK) { SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error))); + + if (i->client_entry) { + i->client_entry->internal.resolve_cmd_ident = 0; + silc_client_unref_client(client, conn, i->client_entry); + } + if (i->completion) i->completion(client, conn, error, NULL, i->context); goto out; @@ -170,6 +177,12 @@ static SilcBool silc_client_get_clients_cb(SilcClient client, /* Deliver the clients to the caller */ if (i->completion) { SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients))); + + if (i->client_entry) { + i->client_entry->internal.resolve_cmd_ident = 0; + silc_client_unref_client(client, conn, i->client_entry); + } + silc_dlist_start(i->clients); i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context); } @@ -236,10 +249,13 @@ silc_client_get_client_by_id_resolve(SilcClient client, if (!cmd_ident && completion) completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context); - if (client_entry && cmd_ident) + if (client_entry && cmd_ident) { client_entry->internal.resolve_cmd_ident = cmd_ident; + i->client_entry = client_entry; + } else { + silc_client_unref_client(client, conn, client_entry); + } - silc_client_unref_client(client, conn, client_entry); silc_buffer_free(idp); return cmd_ident; @@ -699,7 +715,6 @@ SilcClientEntry silc_client_add_client(SilcClient client, silc_rwlock_alloc(&client_entry->internal.lock); silc_atomic_init8(&client_entry->internal.refcnt, 0); client_entry->id = *id; - client_entry->internal.valid = TRUE; client_entry->mode = mode; client_entry->realname = userinfo ? strdup(userinfo) : NULL; silc_parse_userfqdn(nickname, client_entry->nickname, @@ -752,6 +767,9 @@ SilcClientEntry silc_client_add_client(SilcClient client, /* Format the nickname */ silc_client_nickname_format(client, conn, client_entry, FALSE); + if (client_entry->nickname[0]) + client_entry->internal.valid = TRUE; + SILC_LOG_DEBUG(("Added %p", client_entry)); return client_entry; @@ -806,6 +824,7 @@ void silc_client_update_client(SilcClient client, client_entry, NULL, nick, TRUE); silc_mutex_unlock(conn->internal->lock); client_entry->nickname_normalized = nick; + client_entry->internal.valid = TRUE; } client_entry->mode = mode; @@ -860,6 +879,7 @@ SilcBool silc_client_change_nickname(SilcClient client, 0, NULL); } + client_entry->internal.valid = TRUE; return TRUE; } @@ -924,6 +944,27 @@ SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn, return ret; } +/* Internal routine used to find client by ID and if not found this creates + new client entry and returns it. */ + +SilcClientEntry silc_client_get_client(SilcClient client, + SilcClientConnection conn, + SilcClientID *client_id) +{ + SilcClientEntry client_entry; + + client_entry = silc_client_get_client_by_id(client, conn, client_id); + if (!client_entry) { + client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL, + client_id, 0); + if (!client_entry) + return NULL; + silc_client_ref_client(client, conn, client_entry); + } + + return client_entry; +} + /* Lock client */ void silc_client_lock_client(SilcClientEntry client_entry) @@ -998,13 +1039,13 @@ SilcClientEntry silc_client_nickname_format(SilcClient client, SilcDList clients; SilcClientEntry entry, unformatted = NULL; - SILC_LOG_DEBUG(("Format nickname")); - if (!client->internal->params->nickname_format[0]) return client_entry; if (!client_entry->nickname[0]) return NULL; + SILC_LOG_DEBUG(("Format nickname")); + /* Get all clients with same nickname. Do not perform the formatting if there aren't any clients with same nickname unless the application is forcing us to do so. */ diff --git a/lib/silcclient/client_entry.h b/lib/silcclient/client_entry.h index bdd70f26..ee785f2d 100644 --- a/lib/silcclient/client_entry.h +++ b/lib/silcclient/client_entry.h @@ -44,11 +44,9 @@ void silc_client_del_client_entry(SilcClient client, SilcClientEntry client_entry); SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry); -SilcClientEntry silc_idlist_get_client(SilcClient client, +SilcClientEntry silc_client_get_client(SilcClient client, SilcClientConnection conn, - const char *nickname, - const char *format, - bool query); + SilcClientID *client_id); SilcChannelEntry silc_client_add_channel(SilcClient client, SilcClientConnection conn, const char *channel_name, diff --git a/lib/silcclient/client_internal.h b/lib/silcclient/client_internal.h index 414aa4cf..17d1fad0 100644 --- a/lib/silcclient/client_internal.h +++ b/lib/silcclient/client_internal.h @@ -142,6 +142,7 @@ struct SilcClientConnectionInternalStruct { SilcIDCache channel_cache; /* Channel entry cache */ SilcIDCache server_cache; /* Server entry cache */ + SilcUInt32 remote_version; /* Remote SILC protocol version */ SilcAtomic16 cmd_ident; /* Current command identifier */ SilcUInt8 retry_count; /* Packet retry counter */ SilcUInt8 retry_timer; /* Packet retry timer */ diff --git a/lib/silcclient/client_keyagr.c b/lib/silcclient/client_keyagr.c index efabe758..15d0f130 100644 --- a/lib/silcclient/client_keyagr.c +++ b/lib/silcclient/client_keyagr.c @@ -661,7 +661,7 @@ SILC_FSM_STATE(silc_client_key_agreement) /* Check whether we know this client already */ remote_client = silc_client_get_client_by_id(client, conn, &remote_id); - if (!remote_client || !remote_client->nickname[0]) { + if (!remote_client || !remote_client->internal.valid) { /** Resolve client info */ silc_client_unref_client(client, conn, remote_client); SILC_FSM_CALL(silc_client_get_client_by_id_resolve( diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index a492931b..40a66d3e 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -28,15 +28,21 @@ /* Notify processing context */ typedef struct { - SilcPacket packet; - SilcNotifyPayload payload; - SilcFSMThread fsm; - SilcChannelEntry channel; + SilcPacket packet; /* Notify packet */ + SilcNotifyPayload payload; /* Parsed notify payload */ + SilcFSMThread fsm; /* Notify FSM thread */ + SilcChannelEntry channel; /* Channel entry being resolved */ + SilcClientEntry client_entry; /* Client entry being resolved */ } *SilcClientNotify; /************************ Static utility functions **************************/ -/* Entry resolving callback. This will continue processing the notify. */ +/* The client entires in notify processing are resolved if they do not exist, + or they are not valid. We go to this callback after resolving where we + check if the client entry has become valid. If resolving succeeded the + entry is valid but remains invalid if resolving failed. This callback + will continue processing the notify. We use this callback also with other + entry resolving. */ static void silc_client_notify_resolved(SilcClient client, SilcClientConnection conn, @@ -46,8 +52,14 @@ static void silc_client_notify_resolved(SilcClient client, { SilcClientNotify notify = context; - /* If no entries found, just finish the notify processing, a silent error */ - if (!entries) + /* If entry is still invalid, resolving failed. Finish notify processing. */ + if (notify->client_entry && !notify->client_entry->internal.valid) { + silc_fsm_next(notify->fsm, silc_client_notify_processed); + silc_client_unref_client(client, conn, notify->client_entry); + } + + /* If no entries found, just finish the notify processing */ + if (!entries && !notify->client_entry) silc_fsm_next(notify->fsm, silc_client_notify_processed); if (notify->channel) { @@ -209,7 +221,7 @@ SILC_FSM_STATE(silc_client_notify) break; } - return SILC_FSM_YIELD; + return SILC_FSM_CONTINUE; } /* Notify processed, finish the packet processing thread */ @@ -298,7 +310,7 @@ SILC_FSM_STATE(silc_client_notify_invite) /* Find Client entry and if not found query it */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry || !client_entry->nickname[0]) { + if (!client_entry || !client_entry->internal.valid) { /** Resolve client */ silc_client_unref_client(client, conn, client_entry); notify->channel = channel; @@ -364,17 +376,21 @@ SILC_FSM_STATE(silc_client_notify_join) if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL)) goto out; - /* Find Client entry and if not found query it */ - client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry || !client_entry->nickname[0] || + /* Find client entry and if not found query it. If we just queried it + don't do it again, unless some data (like username) is missing. */ + client_entry = notify->client_entry; + if (!client_entry) + client_entry = silc_client_get_client(client, conn, &id.u.client_id); + if (!client_entry || !client_entry->internal.valid || !client_entry->username[0]) { /** Resolve client */ - silc_client_unref_client(client, conn, client_entry); notify->channel = channel; + notify->client_entry = client_entry; SILC_FSM_CALL(channel->internal.resolve_cmd_ident = silc_client_get_client_by_id_resolve( - client, conn, &id.u.client_id, NULL, - silc_client_notify_resolved, + client, conn, client_entry ? + &client_entry->id : &id.u.client_id, + NULL, silc_client_notify_resolved, notify)); /* NOT REACHED */ } @@ -455,7 +471,8 @@ SILC_FSM_STATE(silc_client_notify_leave) goto out; /* Remove client from channel */ - silc_client_remove_from_channel(client, conn, channel, client_entry); + if (!silc_client_remove_from_channel(client, conn, channel, client_entry)) + goto out; /* Notify application. */ NOTIFY(client, conn, type, client_entry, channel); @@ -505,7 +522,8 @@ SILC_FSM_STATE(silc_client_notify_signoff) tmp[128] = '\0'; /* Notify application */ - NOTIFY(client, conn, type, client_entry, tmp); + if (client_entry->internal.valid) + NOTIFY(client, conn, type, client_entry, tmp); /* Remove from channel */ if (packet->dst_id_type == SILC_ID_CHANNEL) { @@ -520,6 +538,7 @@ SILC_FSM_STATE(silc_client_notify_signoff) } /* Delete client */ + client_entry->internal.valid = FALSE; silc_client_del_client(client, conn, client_entry); silc_client_unref_client(client, conn, client_entry); @@ -571,7 +590,7 @@ SILC_FSM_STATE(silc_client_notify_topic_set) /* NOT REACHED */ } - /* Get ID */ + /* Get ID of topic changer */ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL)) goto out; @@ -582,18 +601,26 @@ SILC_FSM_STATE(silc_client_notify_topic_set) if (id.type == SILC_ID_CLIENT) { /* Find Client entry */ - client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry || !client_entry->nickname[0]) { - /** Resolve client */ - silc_client_unref_client(client, conn, client_entry); - notify->channel = channel; - SILC_FSM_CALL(channel->internal.resolve_cmd_ident = - silc_client_get_client_by_id_resolve( + client_entry = notify->client_entry; + if (!client_entry) { + client_entry = silc_client_get_client(client, conn, &id.u.client_id); + if (!client_entry || !client_entry->internal.valid) { + /** Resolve client */ + notify->channel = channel; + notify->client_entry = client_entry; + SILC_FSM_CALL(channel->internal.resolve_cmd_ident = + silc_client_get_client_by_id_resolve( client, conn, &id.u.client_id, NULL, silc_client_notify_resolved, notify)); - /* NOT REACHED */ + /* NOT REACHED */ + } } + + /* If client is not on channel, ignore this notify */ + if (!silc_client_on_channel(channel, client_entry)) + goto out; + entry = client_entry; } else if (id.type == SILC_ID_SERVER) { /* Find Server entry */ @@ -664,6 +691,7 @@ SILC_FSM_STATE(silc_client_notify_nick_change) unsigned char *tmp, oldnick[128 + 1]; SilcUInt32 tmp_len; SilcID id, id2; + SilcBool valid; SILC_LOG_DEBUG(("Notify: NICK_CHANGE")); @@ -685,17 +713,12 @@ SILC_FSM_STATE(silc_client_notify_nick_change) SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id)) goto out; - /* Find old Client entry */ + /* Find old client entry. If we don't have the entry, we ignore this + notify. */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry || !client_entry->nickname[0]) { - /** Resolve client */ - silc_client_unref_client(client, conn, client_entry); - SILC_FSM_CALL(silc_client_get_client_by_id_resolve( - client, conn, &id.u.client_id, NULL, - silc_client_notify_resolved, - notify)); - /* NOT REACHED */ - } + if (!client_entry) + goto out; + valid = client_entry->internal.valid; /* Take the new nickname */ tmp = silc_argument_get_arg_type(args, 3, &tmp_len); @@ -729,8 +752,10 @@ SILC_FSM_STATE(silc_client_notify_nick_change) silc_rwlock_unlock(client_entry->internal.lock); - /* Notify application */ - NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick); + /* Notify application, if client entry is valid. We do not send nick change + notify for entries that were invalid (application doesn't know them). */ + if (valid) + NOTIFY(client, conn, type, client_entry, oldnick, client_entry->nickname); out: /** Notify processed */ @@ -790,24 +815,32 @@ SILC_FSM_STATE(silc_client_notify_cmode_change) goto out; SILC_GET32_MSB(mode, tmp); - /* Get ID */ + /* Get ID of who changed the mode */ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL)) goto out; if (id.type == SILC_ID_CLIENT) { /* Find Client entry */ - client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry || !client_entry->nickname[0]) { - /** Resolve client */ - silc_client_unref_client(client, conn, client_entry); - notify->channel = channel; - SILC_FSM_CALL(channel->internal.resolve_cmd_ident = - silc_client_get_client_by_id_resolve( + client_entry = notify->client_entry; + if (!client_entry) { + client_entry = silc_client_get_client(client, conn, &id.u.client_id); + if (!client_entry || !client_entry->internal.valid) { + /** Resolve client */ + notify->channel = channel; + notify->client_entry = client_entry; + SILC_FSM_CALL(channel->internal.resolve_cmd_ident = + silc_client_get_client_by_id_resolve( client, conn, &id.u.client_id, NULL, silc_client_notify_resolved, notify)); - /* NOT REACHED */ + /* NOT REACHED */ + } } + + /* If client is not on channel, ignore this notify */ + if (!silc_client_on_channel(channel, client_entry)) + goto out; + entry = client_entry; } else if (id.type == SILC_ID_SERVER) { /* Find Server entry */ @@ -973,7 +1006,7 @@ SILC_FSM_STATE(silc_client_notify_cumode_change) /* 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]) { + if (!client_entry2 || !client_entry2->internal.valid) { /** Resolve client */ silc_client_unref_client(client, conn, client_entry2); SILC_FSM_CALL(silc_client_get_client_by_id_resolve( @@ -983,30 +1016,42 @@ SILC_FSM_STATE(silc_client_notify_cumode_change) /* NOT REACHED */ } + /* If target client is not on channel, ignore this notify */ + if (!silc_client_on_channel(channel, client_entry2)) + goto out; + /* Get the mode */ tmp = silc_argument_get_arg_type(args, 2, &tmp_len); if (!tmp) goto out; SILC_GET32_MSB(mode, tmp); - /* Get ID */ + /* Get ID of mode changer */ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL)) goto out; if (id.type == SILC_ID_CLIENT) { /* Find Client entry */ - client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry || !client_entry->nickname[0]) { - /** Resolve client */ - silc_client_unref_client(client, conn, client_entry); - notify->channel = channel; - SILC_FSM_CALL(channel->internal.resolve_cmd_ident = - silc_client_get_client_by_id_resolve( + client_entry = notify->client_entry; + if (!client_entry) { + client_entry = silc_client_get_client(client, conn, &id.u.client_id); + if (!client_entry || !client_entry->internal.valid) { + /** Resolve client */ + notify->channel = channel; + notify->client_entry = client_entry; + SILC_FSM_CALL(channel->internal.resolve_cmd_ident = + silc_client_get_client_by_id_resolve( client, conn, &id.u.client_id, NULL, silc_client_notify_resolved, notify)); - /* NOT REACHED */ + /* NOT REACHED */ + } } + + /* If client is not on channel, ignore this notify */ + if (!silc_client_on_channel(channel, client_entry)) + goto out; + entry = client_entry; } else if (id.type == SILC_ID_SERVER) { /* Find Server entry */ @@ -1190,11 +1235,11 @@ SILC_FSM_STATE(silc_client_notify_kicked) /* NOT REACHED */ } - /* Get Client ID */ + /* Get the kicked Client ID */ if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL)) goto out; - /* Find Client entry */ + /* Find client entry */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); if (!client_entry) goto out; @@ -1205,7 +1250,7 @@ SILC_FSM_STATE(silc_client_notify_kicked) /* Find kicker's client entry and if not found resolve it */ client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry2 || !client_entry2->nickname[0]) { + if (!client_entry2 || !client_entry2->internal.valid) { /** Resolve client */ silc_client_unref_client(client, conn, client_entry); silc_client_unref_client(client, conn, client_entry2); @@ -1222,8 +1267,10 @@ SILC_FSM_STATE(silc_client_notify_kicked) tmp = silc_argument_get_arg_type(args, 2, &tmp_len); /* Remove kicked client from channel */ - if (client_entry != conn->local_entry) - silc_client_remove_from_channel(client, conn, channel, client_entry); + if (client_entry != conn->local_entry) { + if (!silc_client_remove_from_channel(client, conn, channel, client_entry)) + goto out; + } /* Notify application. */ NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel); @@ -1288,7 +1335,7 @@ SILC_FSM_STATE(silc_client_notify_killed) /* Find Client entry */ client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry2 || !client_entry2->nickname[0]) { + if (!client_entry2 || !client_entry2->internal.valid) { /** Resolve client */ silc_client_unref_client(client, conn, client_entry); silc_client_unref_client(client, conn, client_entry2); @@ -1332,6 +1379,7 @@ SILC_FSM_STATE(silc_client_notify_killed) /* Delete the killed client */ if (client_entry != conn->local_entry) { silc_client_remove_from_channels(client, conn, client_entry); + client_entry->internal.valid = FALSE; silc_client_del_client(client, conn, client_entry); } @@ -1379,7 +1427,7 @@ SILC_FSM_STATE(silc_client_notify_server_signoff) /* Get the client entry */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (client_entry) + if (client_entry && client_entry->internal.valid) silc_dlist_add(clients, client_entry); } @@ -1391,6 +1439,7 @@ SILC_FSM_STATE(silc_client_notify_server_signoff) silc_dlist_start(clients); while ((client_entry = silc_dlist_get(clients))) { silc_client_remove_from_channels(client, conn, client_entry); + client_entry->internal.valid = FALSE; silc_client_del_client(client, conn, client_entry); } @@ -1474,9 +1523,9 @@ SILC_FSM_STATE(silc_client_notify_watch) if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL)) goto out; - /* Find Client entry and if not found resolve it */ + /* Find client entry and if not found resolve it */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry || !client_entry->nickname[0]) { + if (!client_entry || !client_entry->internal.valid) { /** Resolve client */ silc_client_unref_client(client, conn, client_entry); SILC_FSM_CALL(silc_client_get_client_by_id_resolve( diff --git a/lib/silcclient/client_prvmsg.c b/lib/silcclient/client_prvmsg.c index 20370cf6..9b6002e5 100644 --- a/lib/silcclient/client_prvmsg.c +++ b/lib/silcclient/client_prvmsg.c @@ -36,6 +36,7 @@ SilcBool silc_client_send_private_message(SilcClient client, { SilcBuffer buffer; SilcBool ret; + SilcID sid, rid; if (silc_unlikely(!client || !conn || !client_entry)) return FALSE; @@ -46,6 +47,11 @@ SilcBool silc_client_send_private_message(SilcClient client, SILC_LOG_DEBUG(("Sending private message")); + sid.type = SILC_ID_CLIENT; + sid.u.client_id = conn->local_entry->id; + rid.type = SILC_ID_CLIENT; + rid.u.client_id = client_entry->id; + /* Encode private message payload */ buffer = silc_message_payload_encode(flags, data, data_len, @@ -54,7 +60,7 @@ SilcBool silc_client_send_private_message(SilcClient client, TRUE, client_entry->internal.send_key, client_entry->internal.hmac_send, client->rng, NULL, conn->private_key, - hash, NULL); + hash, &sid, &rid, NULL); if (silc_unlikely(!buffer)) { SILC_LOG_ERROR(("Error encoding private message")); return FALSE; @@ -142,6 +148,8 @@ SILC_FSM_STATE(silc_client_private_message) TRUE, !remote_client->internal.generated, remote_client->internal.receive_key, remote_client->internal.hmac_receive, + packet->src_id, packet->src_id_len, + packet->dst_id, packet->dst_id_len, NULL, FALSE, NULL); if (silc_unlikely(!payload)) goto out; @@ -265,6 +273,8 @@ SilcBool silc_client_private_message_wait(SilcClient client, TRUE, !client_entry->internal.generated, client_entry->internal.receive_key, client_entry->internal.hmac_receive, + packet->src_id, packet->src_id_len, + packet->dst_id, packet->dst_id_len, NULL, FALSE, NULL); if (!(*payload)) { silc_packet_free(packet); @@ -316,13 +326,15 @@ static void silc_client_private_message_key_cb(SilcClient client, SilcDList clients, void *context) { - SilcPacket packet = context; + SilcFSMThread thread = context; + SilcPacket packet = silc_fsm_get_state_context(thread); unsigned char *cipher = NULL, *hmac = NULL; SilcClientEntry client_entry; int ret; if (!clients) { silc_packet_free(packet); + silc_fsm_finish(thread); return; } @@ -347,6 +359,7 @@ static void silc_client_private_message_key_cb(SilcClient client, silc_free(cipher); silc_free(hmac); silc_packet_free(packet); + silc_fsm_finish(thread); } /* Processes incoming Private Message Key payload to indicate that the diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 99fe98a9..72a9c38f 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -797,7 +797,7 @@ SILC_FSM_STATE(silc_client_command_identify) SILC_FSM_STATE(silc_client_command_nick) { - SilcClientCommandContext cmd = fsm_context; + SilcClientCommandContext cmd2, cmd = fsm_context; SilcClientConnection conn = cmd->conn; if (cmd->argc < 2) { @@ -825,6 +825,19 @@ SILC_FSM_STATE(silc_client_command_nick) goto out; } + /* If JOIN command is active, wait for it to finish before sending NICK. + To avoid problems locally with changing IDs while joining, we do this. */ + silc_mutex_lock(conn->internal->lock); + silc_list_start(conn->internal->pending_commands); + while ((cmd2 = silc_list_get(conn->internal->pending_commands))) { + if (cmd2->cmd == SILC_COMMAND_JOIN) { + silc_mutex_unlock(conn->internal->lock); + silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000); + return SILC_FSM_WAIT; + } + } + silc_mutex_unlock(conn->internal->lock); + if (cmd->argv_lens[1] > 128) cmd->argv_lens[1] = 128; @@ -1274,7 +1287,7 @@ SILC_FSM_STATE(silc_client_command_ping) SILC_FSM_STATE(silc_client_command_join) { - SilcClientCommandContext cmd = fsm_context; + SilcClientCommandContext cmd2, cmd = fsm_context; SilcClientConnection conn = cmd->conn; SilcClient client = conn->client; SilcChannelEntry channel = NULL; @@ -1292,6 +1305,19 @@ SILC_FSM_STATE(silc_client_command_join) if (channel && silc_client_on_channel(channel, conn->local_entry)) goto out; + /* If NICK command is active, wait for it to finish before sending JOIN. + To avoid problems locally with changing IDs while joining, we do this. */ + silc_mutex_lock(conn->internal->lock); + silc_list_start(conn->internal->pending_commands); + while ((cmd2 = silc_list_get(conn->internal->pending_commands))) { + if (cmd2->cmd == SILC_COMMAND_NICK) { + silc_mutex_unlock(conn->internal->lock); + silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000); + return SILC_FSM_WAIT; + } + } + silc_mutex_unlock(conn->internal->lock); + if (cmd->argv_lens[1] > 256) cmd->argv_lens[1] = 256; diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index cf4e151b..80e85186 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -1217,7 +1217,7 @@ SILC_FSM_STATE(silc_client_command_reply_join) /* Get client entry */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry) + if (!client_entry || !client_entry->internal.valid) continue; /* Join client to the channel */ @@ -1921,7 +1921,7 @@ SILC_FSM_STATE(silc_client_command_reply_users) /* Save the client on this channel. Unknown clients are ignored as they 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) { + if (client_entry && client_entry->internal.valid) { silc_rwlock_wrlock(client_entry->internal.lock); silc_client_add_to_channel(client, conn, channel, client_entry, mode); silc_rwlock_unlock(client_entry->internal.lock); diff --git a/lib/silcclient/silcclient_entry.h b/lib/silcclient/silcclient_entry.h index 56cbaf7d..0f4e996a 100644 --- a/lib/silcclient/silcclient_entry.h +++ b/lib/silcclient/silcclient_entry.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2006 Pekka Riikonen + Copyright (C) 2006 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -208,10 +208,17 @@ typedef void (*SilcGetClientCallback)(SilcClient client, * before reading any data from the `client_entry'. The lock must be * unlocked with silc_client_unlock_client. * + * NOTES + * * The entry must be unlocked before calling any Client Library API - * functions where the entry is given as argument. + * functions where the entry is given as argument, unless otherwise stated. + * + * The entry should not be locked for long periods of time. For example, + * it is not appropriate to hold the lock while waiting user interface to + * be drawn. The appropriate way is to read the data and duplicate it if + * necessary, unlock the entry, then draw on the user interface. * - * This function is not needed if application is not multithreaded + * This function is not needed if application is not multithreaded. * ***/ void silc_client_lock_client(SilcClientEntry client_entry); @@ -245,7 +252,7 @@ void silc_client_unlock_client(SilcClientEntry client_entry); * after it is not needed anymore. Returns `client_entry'. * ***/ -SilcClientEntry silc_client_ref_client(SilcClient client, +SilcClientEntry silc_client_ref_client(SilcClient client, SilcClientConnection conn, SilcClientEntry client_entry); @@ -595,10 +602,17 @@ typedef void (*SilcGetChannelCallback)(SilcClient client, * before reading any data from the `channel_entry'. The lock must be * unlocked with silc_client_unlock_channel. * + * NOTES + * * The entry must be unlocked before calling any Client Library API - * functions where the entry is given as argument. + * functions where the entry is given as argument, unless otherwise stated. * - * This function is not needed if application is not multithreaded + * The entry should not be locked for long periods of time. For example, + * it is not appropriate to hold the lock while waiting user interface to + * be drawn. The appropriate way is to read the data and duplicate it if + * necessary, unlock the entry, then draw on the user interface. + * + * This function is not needed if application is not multithreaded. * ***/ void silc_client_lock_channel(SilcChannelEntry channel_entry); @@ -632,7 +646,7 @@ void silc_client_unlock_channel(SilcChannelEntry channel_entry); * after it is not needed anymore. Returns `channel_entry'. * ***/ -SilcChannelEntry silc_client_ref_channel(SilcClient client, +SilcChannelEntry silc_client_ref_channel(SilcClient client, SilcClientConnection conn, SilcChannelEntry channel_entry); @@ -826,10 +840,17 @@ typedef void (*SilcGetServerCallback)(SilcClient client, * before reading any data from the `server_entry'. The lock must be * unlocked with silc_client_unlock_server. * + * NOTES + * * The entry must be unlocked before calling any Client Library API - * functions where the entry is given as argument. + * functions where the entry is given as argument, unless otherwise stated. + * + * The entry should not be locked for long periods of time. For example, + * it is not appropriate to hold the lock while waiting user interface to + * be drawn. The appropriate way is to read the data and duplicate it if + * necessary, unlock the entry, then draw on the user interface. * - * This function is not needed if application is not multithreaded + * This function is not needed if application is not multithreaded. * ***/ void silc_client_lock_server(SilcServerEntry server_entry); @@ -863,7 +884,7 @@ void silc_client_unlock_server(SilcServerEntry server_entry); * after it is not needed anymore. Returns `server_entry'. * ***/ -SilcServerEntry silc_client_ref_server(SilcClient client, +SilcServerEntry silc_client_ref_server(SilcClient client, SilcClientConnection conn, SilcServerEntry server_entry); -- 2.24.0