5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2006 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Private Message Send ****************************/
27 /* Sends private message to remote client. */
29 SilcBool silc_client_send_private_message(SilcClient client,
30 SilcClientConnection conn,
31 SilcClientEntry client_entry,
32 SilcMessageFlags flags,
40 if (silc_unlikely(!client || !conn || !client_entry))
42 if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
44 if (silc_unlikely(conn->internal->disconnected))
47 SILC_LOG_DEBUG(("Sending private message"));
49 /* Encode private message payload */
51 silc_message_payload_encode(flags, data, data_len,
52 (!client_entry->internal.send_key ? FALSE :
53 !client_entry->internal.generated),
54 TRUE, client_entry->internal.send_key,
55 client_entry->internal.hmac_send,
56 client->rng, NULL, conn->private_key,
58 if (silc_unlikely(!buffer)) {
59 SILC_LOG_ERROR(("Error encoding private message"));
63 /* Send the private message packet */
64 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_PRIVATE_MESSAGE,
65 client_entry->internal.send_key ?
66 SILC_PACKET_FLAG_PRIVMSG_KEY : 0,
67 0, NULL, SILC_ID_CLIENT, &client_entry->id,
68 silc_buffer_datalen(buffer), NULL, NULL);
70 silc_buffer_free(buffer);
74 /************************* Private Message Receive **************************/
76 /* Private message waiting context */
80 SilcDList message_queue;
81 unsigned int stopped : 1;
82 } *SilcClientPrivateMessageWait;
84 /* Client resolving callback. Continues with the private message processing */
86 static void silc_client_private_message_resolved(SilcClient client,
87 SilcClientConnection conn,
92 /* If no client found, ignore the private message, a silent error */
94 silc_fsm_next(context, silc_client_private_message_error);
96 /* Continue processing the private message packet */
97 SILC_FSM_CALL_CONTINUE(context);
100 /* Private message received. */
102 SILC_FSM_STATE(silc_client_private_message)
104 SilcClientConnection conn = fsm_context;
105 SilcClient client = conn->client;
106 SilcPacket packet = state_context;
107 SilcMessagePayload payload = NULL;
108 SilcClientID remote_id;
109 SilcClientEntry remote_client = NULL;
110 SilcMessageFlags flags;
111 unsigned char *message;
112 SilcUInt32 message_len;
113 SilcClientPrivateMessageWait pmw;
115 SILC_LOG_DEBUG(("Received private message"));
117 if (silc_unlikely(packet->src_id_type != SILC_ID_CLIENT)) {
118 /** Invalid packet */
119 silc_fsm_next(fsm, silc_client_private_message_error);
120 return SILC_FSM_CONTINUE;
123 if (silc_unlikely(!silc_id_str2id(packet->src_id, packet->src_id_len,
124 SILC_ID_CLIENT, &remote_id,
125 sizeof(remote_id)))) {
126 /** Invalid source ID */
127 silc_fsm_next(fsm, silc_client_private_message_error);
128 return SILC_FSM_CONTINUE;
131 /* Check whether we know this client already */
132 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
133 if (!remote_client || !remote_client->nickname[0]) {
134 /** Resolve client info */
135 silc_client_unref_client(client, conn, remote_client);
136 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
137 client, conn, &remote_id, NULL,
138 silc_client_private_message_resolved,
143 if (silc_unlikely(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
144 !remote_client->internal.receive_key &&
145 !remote_client->internal.hmac_receive))
148 /* Parse the payload and decrypt it also if private message key is set */
150 silc_message_payload_parse(silc_buffer_datalen(&packet->buffer),
151 TRUE, !remote_client->internal.generated,
152 remote_client->internal.receive_key,
153 remote_client->internal.hmac_receive,
155 if (silc_unlikely(!payload))
158 #if 0 /* We need to rethink this. This doesn't work with multiple
159 waiters, and performance is suboptimal. */
160 /* Check if some thread is waiting for this private message */
161 silc_mutex_lock(conn->internal->lock);
162 if (conn->internal->privmsg_wait &&
163 silc_hash_table_find_ext(conn->internal->privmsg_wait,
164 &remote_client->id, NULL, (void **)&pmw,
165 NULL, NULL, silc_hash_id_compare_full,
166 SILC_32_TO_PTR(SILC_ID_CLIENT))) {
167 /* Signal that message was received */
168 silc_mutex_unlock(conn->internal->lock);
169 silc_mutex_lock(pmw->wait_lock);
171 silc_dlist_add(pmw->message_queue, payload);
172 silc_cond_broadcast(pmw->wait_cond);
173 silc_mutex_unlock(pmw->wait_lock);
174 silc_packet_free(packet);
177 silc_mutex_unlock(pmw->wait_lock);
179 silc_mutex_unlock(conn->internal->lock);
182 /* Pass the private message to application */
183 flags = silc_message_get_flags(payload);
184 message = silc_message_get_data(payload, &message_len);
185 client->internal->ops->private_message(client, conn, remote_client, payload,
186 flags, message, message_len);
188 /* See if we are away (gone). If we are away we will reply to the
189 sender with the set away message. */
190 if (conn->internal->away && conn->internal->away->away &&
191 !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
192 /* If it's me, ignore */
193 if (SILC_ID_CLIENT_COMPARE(&remote_id, conn->local_id))
196 /* Send the away message */
197 silc_client_send_private_message(client, conn, remote_client,
198 SILC_MESSAGE_FLAG_AUTOREPLY |
199 SILC_MESSAGE_FLAG_NOREPLY, NULL,
200 conn->internal->away->away,
201 strlen(conn->internal->away->away));
205 /** Packet processed */
206 silc_packet_free(packet);
207 silc_client_unref_client(client, conn, remote_client);
209 silc_message_payload_free(payload);
210 return SILC_FSM_FINISH;
213 /* Private message error. */
215 SILC_FSM_STATE(silc_client_private_message_error)
217 SilcPacket packet = state_context;
218 silc_packet_free(packet);
219 return SILC_FSM_FINISH;
222 #if 0 /* XXX we need to rethink this */
223 /* Initialize private message waiting in a thread. */
225 void *silc_client_private_message_wait_init(SilcClientConnection conn,
226 SilcClientEntry client_entry)
228 SilcClientPrivateMessageWait pmw;
230 pmw = silc_calloc(1, sizeof(*pmw));
234 pmw->message_queue = silc_dlist_init();
235 if (!pmw->message_queue) {
240 /* Allocate mutex and conditional variable */
241 if (!silc_mutex_alloc(&pmw->wait_lock)) {
242 silc_dlist_uninit(pmw->message_queue);
246 if (!silc_cond_alloc(&pmw->wait_cond)) {
247 silc_dlist_uninit(pmw->message_queue);
248 silc_mutex_free(pmw->wait_lock);
253 silc_mutex_lock(conn->internal->lock);
255 /* Allocate waiting hash table */
256 if (!conn->internal->privmsg_wait) {
257 conn->internal->privmsg_wait =
258 silc_hash_table_alloc(0, silc_hash_id,
259 SILC_32_TO_PTR(SILC_ID_CLIENT),
260 silc_hash_id_compare,
261 SILC_32_TO_PTR(SILC_ID_CLIENT), NULL, NULL, TRUE);
262 if (!conn->internal->privmsg_wait) {
263 silc_mutex_unlock(conn->internal->lock);
264 silc_dlist_uninit(pmw->message_queue);
265 silc_mutex_free(pmw->wait_lock);
266 silc_cond_free(pmw->wait_cond);
272 /* Add to waiting hash table */
273 silc_hash_table_add(conn->internal->privmsg_wait, client_entry->id, pmw);
275 silc_mutex_unlock(conn->internal->lock);
280 /* Uninitialize private message waiting. */
282 void silc_client_private_message_wait_uninit(SilcClientConnection conn,
283 SilcClientEntry client_entry,
286 SilcClientPrivateMessageWait pmw = waiter;
287 SilcMessagePayload payload;
289 /* Signal any threads to stop waiting */
290 silc_mutex_lock(pmw->wait_lock);
292 silc_cond_broadcast(pmw->wait_cond);
293 silc_mutex_unlock(pmw->wait_lock);
295 /* Re-acquire lock and free resources */
296 silc_mutex_lock(pmw->wait_lock);
298 /* Free any remaining message */
299 silc_dlist_start(pmw->message_queue);
300 while ((payload = silc_dlist_get(pmw->message_queue)))
301 silc_message_payload_free(payload);
303 silc_dlist_uninit(pmw->message_queue);
304 silc_cond_free(pmw->wait_cond);
305 silc_mutex_unlock(pmw->wait_lock);
306 silc_mutex_free(pmw->wait_lock);
308 silc_mutex_lock(conn->internal->lock);
309 silc_hash_table_del_by_context(conn->internal->privmsg_wait,
310 client_entry->id, pmw);
311 silc_mutex_unlock(conn->internal->lock);
316 /* Blocks the calling process or thread until a private message has been
317 received from the specified client. */
319 SilcBool silc_client_private_message_wait(SilcClientConnection conn,
320 SilcClientEntry client_entry,
322 SilcMessagePayload *payload)
324 SilcClientPrivateMessageWait pmw = waiter;
327 silc_mutex_lock(pmw->wait_lock);
329 /* Wait here until private message has been received */
330 while (silc_dlist_count(pmw->message_queue) == 0) {
332 silc_mutex_unlock(pmw->wait_lock);
335 silc_cond_wait(pmw->wait_cond, pmw->wait_lock);
339 silc_dlist_start(pmw->message_queue);
340 *payload = silc_dlist_get(pmw->message_queue);
341 silc_dlist_del(pmw->message_queue, *payload);
343 silc_mutex_unlock(pmw->wait_lock);
349 /*************************** Private Message Key ****************************/
351 /* Sends private message key request. Sender of this packet is initiator
352 when setting the private message key. */
355 silc_client_send_private_message_key_request(SilcClient client,
356 SilcClientConnection conn,
357 SilcClientEntry client_entry)
359 const char *cipher, *hmac;
361 SILC_LOG_DEBUG(("Sending private message key request"));
363 cipher = silc_cipher_get_name(client_entry->internal.send_key);
364 hmac = silc_hmac_get_name(client_entry->internal.hmac_send);
366 /* Send the packet */
367 return silc_packet_send_va_ext(conn->stream,
368 SILC_PACKET_PRIVATE_MESSAGE_KEY,
369 0, 0, NULL, SILC_ID_CLIENT,
370 &client_entry->id, NULL, NULL,
371 SILC_STR_UI_SHORT(strlen(cipher)),
372 SILC_STR_DATA(cipher, strlen(cipher)),
373 SILC_STR_UI_SHORT(strlen(hmac)),
374 SILC_STR_DATA(hmac, strlen(hmac)),
378 /* Client resolving callback. Here we simply mark that we are the responder
379 side of this private message key request. */
381 static void silc_client_private_message_key_cb(SilcClient client,
382 SilcClientConnection conn,
387 SilcPacket packet = context;
388 unsigned char *cipher = NULL, *hmac = NULL;
389 SilcClientEntry client_entry;
393 silc_packet_free(packet);
397 /* Parse the private message key payload */
398 ret = silc_buffer_unformat(&packet->buffer,
399 SILC_STR_UI16_STRING_ALLOC(&cipher),
400 SILC_STR_UI16_STRING_ALLOC(&hmac),
405 /* Mark that we are responder */
406 client_entry = silc_dlist_get(clients);
407 client_entry->internal.prv_resp = TRUE;
409 /* XXX we should notify application that remote wants to set up the
410 static key. And we should tell if we already have key with remote.
411 Application should return status telling whether to delete the key
417 silc_packet_free(packet);
420 /* Processes incoming Private Message Key payload to indicate that the
421 sender whishes to set up a static private message key. */
423 SILC_FSM_STATE(silc_client_private_message_key)
425 SilcClientConnection conn = fsm_context;
426 SilcClient client = conn->client;
427 SilcPacket packet = state_context;
428 SilcClientID remote_id;
430 if (packet->src_id_type != SILC_ID_CLIENT) {
431 silc_packet_free(packet);
432 return SILC_FSM_FINISH;
435 if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
436 &remote_id, sizeof(remote_id))) {
437 silc_packet_free(packet);
438 return SILC_FSM_FINISH;
441 /* Always resolve the remote client. The actual packet is processed
442 in the resolving callback. */
443 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
444 client, conn, &remote_id, NULL,
445 silc_client_private_message_key_cb,
449 /* Adds new private message key to `client_entry'. If we are setting this
450 before receiving request for it from `client_entry' we will send the
451 request to the client. Otherwise, we are responder side. */
453 SilcBool silc_client_add_private_message_key(SilcClient client,
454 SilcClientConnection conn,
455 SilcClientEntry client_entry,
461 SilcSKEKeyMaterial keymat;
464 if (!client || !client_entry)
467 /* Return FALSE if key already set */
468 if (client_entry->internal.send_key && client_entry->internal.receive_key)
472 cipher = SILC_DEFAULT_CIPHER;
474 hmac = SILC_DEFAULT_HMAC;
476 /* Check the requested cipher and HMAC */
477 if (!silc_cipher_is_supported(cipher))
479 if (!silc_hmac_is_supported(hmac))
483 client_entry->internal.key = silc_memdup(key, key_len);
484 client_entry->internal.key_len = key_len;
486 /* Produce the key material as the protocol defines */
487 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
488 conn->internal->sha1hash);
492 /* Set the key into use */
493 ret = silc_client_add_private_message_key_ske(client, conn, client_entry,
494 cipher, hmac, keymat);
495 client_entry->internal.generated = FALSE;
497 /* Free the key material */
498 silc_ske_free_key_material(keymat);
500 /* If we are setting the key without a request from the remote client,
501 we will send request to remote. */
502 if (!client_entry->internal.prv_resp)
503 silc_client_send_private_message_key_request(client, conn, client_entry);
508 /* Same as above but takes the key material from the SKE key material
511 SilcBool silc_client_add_private_message_key_ske(SilcClient client,
512 SilcClientConnection conn,
513 SilcClientEntry client_entry,
516 SilcSKEKeyMaterial keymat)
518 if (!client || !client_entry)
521 /* Return FALSE if key already set */
522 if (client_entry->internal.send_key && client_entry->internal.receive_key)
526 cipher = SILC_DEFAULT_CIPHER;
528 hmac = SILC_DEFAULT_HMAC;
530 /* Check the requested cipher and HMAC */
531 if (!silc_cipher_is_supported(cipher))
533 if (!silc_hmac_is_supported(hmac))
536 client_entry->internal.generated = TRUE;
538 /* Allocate the cipher and HMAC */
539 if (!silc_cipher_alloc(cipher, &client_entry->internal.send_key))
541 if (!silc_cipher_alloc(cipher, &client_entry->internal.receive_key))
543 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_send))
545 if (!silc_hmac_alloc(hmac, NULL, &client_entry->internal.hmac_receive))
549 if (client_entry->internal.prv_resp) {
550 silc_cipher_set_key(client_entry->internal.send_key,
551 keymat->receive_enc_key,
552 keymat->enc_key_len, TRUE);
553 silc_cipher_set_iv(client_entry->internal.send_key,
555 silc_cipher_set_key(client_entry->internal.receive_key,
556 keymat->send_enc_key,
557 keymat->enc_key_len, FALSE);
558 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
559 silc_hmac_set_key(client_entry->internal.hmac_send,
560 keymat->receive_hmac_key,
561 keymat->hmac_key_len);
562 silc_hmac_set_key(client_entry->internal.hmac_receive,
563 keymat->send_hmac_key,
564 keymat->hmac_key_len);
566 silc_cipher_set_key(client_entry->internal.send_key,
567 keymat->send_enc_key,
568 keymat->enc_key_len, TRUE);
569 silc_cipher_set_iv(client_entry->internal.send_key,
571 silc_cipher_set_key(client_entry->internal.receive_key,
572 keymat->receive_enc_key,
573 keymat->enc_key_len, FALSE);
574 silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
575 silc_hmac_set_key(client_entry->internal.hmac_send,
576 keymat->send_hmac_key,
577 keymat->hmac_key_len);
578 silc_hmac_set_key(client_entry->internal.hmac_receive,
579 keymat->receive_hmac_key,
580 keymat->hmac_key_len);
586 /* Removes the private message from the library. The key won't be used
587 after this to protect the private messages with the remote `client_entry'
588 client. Returns FALSE on error, TRUE otherwise. */
590 SilcBool silc_client_del_private_message_key(SilcClient client,
591 SilcClientConnection conn,
592 SilcClientEntry client_entry)
594 if (!client || !client_entry)
597 if (!client_entry->internal.send_key && !client_entry->internal.receive_key)
600 silc_cipher_free(client_entry->internal.send_key);
601 silc_cipher_free(client_entry->internal.receive_key);
603 if (client_entry->internal.key) {
604 memset(client_entry->internal.key, 0, client_entry->internal.key_len);
605 silc_free(client_entry->internal.key);
608 client_entry->internal.send_key = NULL;
609 client_entry->internal.receive_key = NULL;
610 client_entry->internal.key = NULL;
611 client_entry->internal.prv_resp = FALSE;
616 /* Returns array of set private message keys associated to the connection
617 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
618 count to the `key_count' argument. The array must be freed by the caller
619 by calling the silc_client_free_private_message_keys function. Note:
620 the keys returned in the array is in raw format. It might not be desired
621 to show the keys as is. The application might choose not to show the keys
622 at all or to show the fingerprints of the keys. */
624 SilcPrivateMessageKeys
625 silc_client_list_private_message_keys(SilcClient client,
626 SilcClientConnection conn,
627 SilcUInt32 *key_count)
629 SilcPrivateMessageKeys keys;
630 SilcUInt32 count = 0;
632 SilcIDCacheEntry id_cache;
633 SilcClientEntry entry;
635 if (!client || !conn)
638 silc_mutex_lock(conn->internal->lock);
639 if (!silc_idcache_get_all(conn->internal->client_cache, &list)) {
640 silc_mutex_unlock(conn->internal->lock);
644 keys = silc_calloc(silc_list_count(list), sizeof(*keys));
646 silc_mutex_unlock(conn->internal->lock);
650 silc_list_start(list);
651 while ((id_cache = silc_list_get(list))) {
652 entry = id_cache->context;
653 if (entry->internal.send_key) {
654 keys[count].client_entry = entry;
655 keys[count].cipher = (char *)silc_cipher_get_name(entry->internal.
657 keys[count].key = (entry->internal.generated == FALSE ?
658 entry->internal.key : NULL);
659 keys[count].key_len = (entry->internal.generated == FALSE ?
660 entry->internal.key_len : 0);
665 silc_mutex_unlock(conn->internal->lock);
673 /* Frees the SilcPrivateMessageKeys array returned by the function
674 silc_client_list_private_message_keys. */
676 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
677 SilcUInt32 key_count)
682 /* Sets away `message'. The away message may be set when the client's
683 mode is changed to SILC_UMODE_GONE and the client whishes to reply
684 to anyone who sends private message. The `message' will be sent
685 automatically back to the the client who send private message. If
686 away message is already set this replaces the old message with the
687 new one. If `message' is NULL the old away message is removed.
688 The sender may freely free the memory of the `message'. */
690 void silc_client_set_away_message(SilcClient client,
691 SilcClientConnection conn,
694 assert(client && conn);
696 if (!message && conn->internal->away) {
697 silc_free(conn->internal->away->away);
698 silc_free(conn->internal->away);
699 conn->internal->away = NULL;
703 if (!conn->internal->away)
704 conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
705 if (conn->internal->away->away)
706 silc_free(conn->internal->away->away);
707 conn->internal->away->away = strdup(message);