5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 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 /************************ Static utility functions **************************/
27 /* Connection machine FSM destructor. This will finish the thread where
28 the machine was running and deletes the connection context. */
30 static void silc_client_connection_destructor(SilcFSM fsm,
32 void *destructor_context)
34 SilcClientConnection conn = fsm_context;
35 SilcFSMThread thread = destructor_context;
37 SILC_LOG_DEBUG(("Connection %p finished", conn));
39 /* Delete connection */
40 silc_client_del_connection(conn->client, conn);
42 /* Finish the thread were this machine was running. Its destructor is the
43 silc_client_connection_finished. */
44 silc_fsm_finish(thread);
47 /* Connection thread FSM destructor. This was the thread where the connection
48 machine was running (may be real thread). From here we notify client
49 that the connection thread has finished. */
51 static void silc_client_connection_finished(SilcFSMThread fsm,
53 void *destructor_context)
55 SilcClient client = silc_fsm_get_state_context(fsm);
57 /* Signal client that we have finished */
58 silc_atomic_sub_int16(&client->internal->conns, 1);
59 client->internal->connection_closed = TRUE;
60 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
65 /* Packet FSM thread destructor */
67 static void silc_client_packet_destructor(SilcFSMThread thread,
69 void *destructor_context)
71 SilcClientConnection conn = thread_context;
73 /* Add thread back to thread pool */
74 silc_list_add(conn->internal->thread_pool, thread);
75 if (silc_list_count(conn->internal->thread_pool) == 1)
76 silc_list_start(conn->internal->thread_pool);
79 /* Packet engine callback to receive a packet */
81 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
82 SilcPacketStream stream,
84 void *callback_context,
87 SilcClientConnection conn = stream_context;
90 /* Packets we do not handle */
91 switch (packet->type) {
92 case SILC_PACKET_HEARTBEAT:
93 case SILC_PACKET_SUCCESS:
94 case SILC_PACKET_FAILURE:
95 case SILC_PACKET_REJECT:
96 case SILC_PACKET_KEY_EXCHANGE:
97 case SILC_PACKET_KEY_EXCHANGE_1:
98 case SILC_PACKET_KEY_EXCHANGE_2:
99 case SILC_PACKET_REKEY_DONE:
100 case SILC_PACKET_CONNECTION_AUTH:
105 /* Get packet processing thread */
106 thread = silc_list_get(conn->internal->thread_pool);
108 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
109 silc_client_packet_destructor, NULL, FALSE);
113 silc_list_del(conn->internal->thread_pool, thread);
114 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
115 silc_client_packet_destructor, NULL, FALSE);
118 /* Process packet in thread */
119 silc_fsm_set_state_context(thread, packet);
120 silc_fsm_start_sync(thread, silc_client_connection_st_packet);
125 /* Packet engine callback to indicate end of stream */
127 static void silc_client_packet_eos(SilcPacketEngine engine,
128 SilcPacketStream stream,
129 void *callback_context,
130 void *stream_context)
132 SilcClientConnection conn = stream_context;
134 SILC_LOG_DEBUG(("Remote disconnected connection"));
136 /* Signal to close connection */
137 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
138 if (!conn->internal->disconnected) {
139 conn->internal->disconnected = TRUE;
140 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
144 /* Packet engine callback to indicate error */
146 static void silc_client_packet_error(SilcPacketEngine engine,
147 SilcPacketStream stream,
148 SilcPacketError error,
149 void *callback_context,
150 void *stream_context)
155 /* Packet stream callbacks */
156 static SilcPacketCallbacks silc_client_stream_cbs =
158 silc_client_packet_receive,
159 silc_client_packet_eos,
160 silc_client_packet_error
165 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
166 void *destructor_context)
171 /* Connect abort operation */
173 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
175 SilcClientConnection conn = context;
177 SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
179 /* Connection callback will not be called after user aborted connecting */
180 conn->callback = NULL;
181 conn->internal->cop = NULL;
183 /* Signal to close connection */
184 if (!conn->internal->disconnected) {
185 conn->internal->disconnected = TRUE;
187 /* If user aborts before connection machine is even up yet, then don't
188 send signal yet. It will process this event when it comes up. */
189 if (silc_fsm_is_started(&conn->internal->fsm))
190 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
194 /************************** Connection's machine ****************************/
196 /* Start the connection's state machine. If threads are in use the machine
197 is always executed in a real thread. */
199 SILC_FSM_STATE(silc_client_connection_st_start)
201 SilcClientConnection conn = fsm_context;
204 /* Take scheduler for connection */
205 conn->internal->schedule = silc_fsm_get_schedule(fsm);
207 /*** Run connection machine */
208 connfsm = &conn->internal->fsm;
209 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
210 fsm, conn->internal->schedule);
211 silc_fsm_event_init(&conn->internal->wait_event, connfsm);
212 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
214 /* Schedule any events possibly set in initialization */
215 if (conn->internal->disconnected)
216 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
217 if (conn->internal->connect)
218 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
219 if (conn->internal->key_exchange)
220 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
222 /* Wait until this thread is terminated from the machine destructor */
223 return SILC_FSM_WAIT;
226 /* Connection machine main state. This handles various connection related
227 events, but not packet processing. It's done in dedicated packet
228 processing FSM thread. */
230 SILC_FSM_STATE(silc_client_connection_st_run)
232 SilcClientConnection conn = fsm_context;
233 SilcFSMThread thread;
235 /* Wait for events */
236 SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
239 thread = &conn->internal->event_thread;
241 if (conn->internal->disconnected) {
242 /** Event: disconnected */
243 SILC_LOG_DEBUG(("Event: disconnected"));
244 silc_fsm_next(fsm, silc_client_connection_st_close);
245 return SILC_FSM_YIELD;
248 if (conn->internal->connect) {
249 SILC_LOG_DEBUG(("Event: connect"));
250 conn->internal->connect = FALSE;
251 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
253 /*** Event: connect */
254 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
256 silc_fsm_start_sync(thread, silc_client_st_connect);
257 return SILC_FSM_CONTINUE;
260 if (conn->internal->key_exchange) {
261 SILC_LOG_DEBUG(("Event: key exchange"));
262 conn->internal->key_exchange = FALSE;
263 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
265 /*** Event: key exchange */
266 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
268 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
269 return SILC_FSM_CONTINUE;
272 if (conn->internal->rekeying) {
273 SILC_LOG_DEBUG(("Event: rekey"));
274 conn->internal->rekeying = FALSE;
275 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
278 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
280 silc_fsm_start_sync(thread, silc_client_st_rekey);
281 return SILC_FSM_CONTINUE;
286 return SILC_FSM_CONTINUE;
289 /* Packet processor thread. Each incoming packet is processed in FSM
290 thread in this state. The thread is run in the connection machine. */
292 SILC_FSM_STATE(silc_client_connection_st_packet)
294 SilcClientConnection conn = fsm_context;
295 SilcPacket packet = state_context;
297 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
299 switch (packet->type) {
301 case SILC_PACKET_PRIVATE_MESSAGE:
302 /** Private message */
303 silc_fsm_next(fsm, silc_client_private_message);
306 case SILC_PACKET_CHANNEL_MESSAGE:
307 /** Channel message */
308 silc_fsm_next(fsm, silc_client_channel_message);
311 case SILC_PACKET_FTP:
312 /* File transfer packet */
313 silc_fsm_next(fsm, silc_client_ftp);
316 case SILC_PACKET_CHANNEL_KEY:
318 silc_fsm_next(fsm, silc_client_channel_key);
321 case SILC_PACKET_COMMAND_REPLY:
323 silc_fsm_next(fsm, silc_client_command_reply);
326 case SILC_PACKET_NOTIFY:
328 silc_fsm_next(fsm, silc_client_notify);
331 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
332 /* Private message key indicator */
333 silc_fsm_next(fsm, silc_client_private_message_key);
336 case SILC_PACKET_DISCONNECT:
338 silc_fsm_next(fsm, silc_client_disconnect);
341 case SILC_PACKET_ERROR:
342 /* Error by server */
343 silc_fsm_next(fsm, silc_client_error);
346 case SILC_PACKET_KEY_AGREEMENT:
348 silc_fsm_next(fsm, silc_client_key_agreement);
351 case SILC_PACKET_COMMAND:
352 /** Command packet */
353 silc_fsm_next(fsm, silc_client_command);
356 case SILC_PACKET_NEW_ID:
358 silc_fsm_next(fsm, silc_client_new_id);
361 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
362 /** Connection auth resolve reply */
363 silc_fsm_next(fsm, silc_client_connect_auth_request);
366 case SILC_PACKET_REKEY:
367 /* Signal to start rekey */
368 conn->internal->rekey_responder = TRUE;
369 conn->internal->rekeying = TRUE;
370 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
372 silc_packet_free(packet);
373 return SILC_FSM_FINISH;
377 silc_packet_free(packet);
378 return SILC_FSM_FINISH;
382 return SILC_FSM_CONTINUE;
385 /* Disconnection event to close remote connection. We close the connection
386 and finish the connection machine in this state. The connection context
387 is deleted in the machine destructor. The connection callback is called
388 in this state if it is set. */
390 SILC_FSM_STATE(silc_client_connection_st_close)
392 SilcClientConnection conn = fsm_context;
393 SilcClientCommandContext cmd;
395 /* Finish running command threads. This will also finish waiting packet
396 thread, as they are always waiting for some command. If any thread is
397 waiting something else than command, they must be finished explicitly. */
398 if (silc_list_count(conn->internal->pending_commands)) {
399 SILC_LOG_DEBUG(("Finish pending commands"));
400 silc_list_start(conn->internal->pending_commands);
401 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
402 if (silc_fsm_is_started(&cmd->thread)) {
403 cmd->verbose = FALSE;
404 silc_fsm_continue_sync(&cmd->thread);
408 /* Give threads time to finish */
409 return SILC_FSM_YIELD;
412 /* Abort ongoing event */
413 if (conn->internal->op) {
414 SILC_LOG_DEBUG(("Abort event"));
415 silc_async_abort(conn->internal->op, NULL, NULL);
416 conn->internal->op = NULL;
419 /* If event thread is running, finish it. */
420 if (silc_fsm_is_started(&conn->internal->event_thread)) {
421 SILC_LOG_DEBUG(("Finish event thread"));
422 silc_fsm_continue_sync(&conn->internal->event_thread);
423 return SILC_FSM_YIELD;
426 /* Call the connection callback */
428 conn->callback(conn->client, conn, conn->internal->status,
429 conn->internal->error, conn->internal->disconnect_message,
430 conn->callback_context);
431 silc_free(conn->internal->disconnect_message);
433 SILC_LOG_DEBUG(("Closing remote connection"));
435 /* Close connection. */
437 silc_packet_stream_destroy(conn->stream);
439 SILC_LOG_DEBUG(("Finishing connection machine"));
440 return SILC_FSM_FINISH;
443 /* Received error packet from server. Send it to application. */
445 SILC_FSM_STATE(silc_client_error)
447 SilcClientConnection conn = fsm_context;
448 SilcClient client = conn->client;
449 SilcPacket packet = state_context;
452 msg = silc_memdup(silc_buffer_data(&packet->buffer),
453 silc_buffer_len(&packet->buffer));
455 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
458 silc_packet_free(packet);
460 return SILC_FSM_FINISH;
463 /* Received disconnect packet from server. We close the connection and
464 send the disconnect message to application. */
466 SILC_FSM_STATE(silc_client_disconnect)
468 SilcClientConnection conn = fsm_context;
469 SilcPacket packet = state_context;
471 char *message = NULL;
473 SILC_LOG_DEBUG(("Server disconnected"));
475 if (silc_buffer_len(&packet->buffer) < 1) {
476 silc_packet_free(packet);
477 return SILC_FSM_FINISH;
480 status = (SilcStatus)packet->buffer.data[0];
482 silc_buffer_pull(&packet->buffer, 1);
483 if (silc_buffer_len(&packet->buffer) > 1 &&
484 silc_utf8_valid(silc_buffer_data(&packet->buffer),
485 silc_buffer_len(&packet->buffer)))
486 message = silc_memdup(silc_buffer_data(&packet->buffer),
487 silc_buffer_len(&packet->buffer));
489 /* Call connection callback */
490 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
491 conn->internal->error = status;
492 conn->internal->disconnect_message = message;
494 /* Signal to close connection */
495 if (!conn->internal->disconnected) {
496 conn->internal->disconnected = TRUE;
497 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
500 silc_packet_free(packet);
502 return SILC_FSM_FINISH;
505 /*************************** Main client machine ****************************/
507 /* The client's main state where we wait for various events */
509 SILC_FSM_STATE(silc_client_st_run)
511 SilcClient client = fsm_context;
513 /* Wait for events */
514 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
518 if (client->internal->run_callback && client->internal->running) {
519 /* Call running callbcak back to application */
520 SILC_LOG_DEBUG(("We are up, call running callback"));
521 client->internal->run_callback = FALSE;
522 client->internal->running(client, client->internal->running_context);
523 return SILC_FSM_CONTINUE;
526 if (client->internal->connection_closed) {
527 /* A connection finished */
528 SILC_LOG_DEBUG(("Event: connection closed"));
529 client->internal->connection_closed = FALSE;
530 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
531 client->internal->stop)
532 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
533 return SILC_FSM_CONTINUE;
536 if (client->internal->stop) {
537 /* Stop client libarry. If we have running connections, wait until
538 they finish first. */
539 if (silc_atomic_get_int16(&client->internal->conns) == 0) {
540 SILC_LOG_DEBUG(("Event: stop"));
541 silc_fsm_next(fsm, silc_client_st_stop);
543 return SILC_FSM_CONTINUE;
548 return SILC_FSM_CONTINUE;
551 /* Stop event. Stops the client library. */
553 SILC_FSM_STATE(silc_client_st_stop)
555 SilcClient client = fsm_context;
557 SILC_LOG_DEBUG(("Client stopped"));
560 silc_schedule_stop(client->schedule);
561 silc_client_commands_unregister(client);
563 /* Call stopped callback to application */
564 if (client->internal->running)
565 client->internal->running(client, client->internal->running_context);
567 return SILC_FSM_FINISH;
570 /******************************* Private API ********************************/
572 /* Adds new connection. Creates the connection context and returns it. */
575 silc_client_add_connection(SilcClient client,
576 SilcConnectionType conn_type,
578 SilcClientConnectionParams *params,
579 SilcPublicKey public_key,
580 SilcPrivateKey private_key,
581 char *remote_host, int port,
582 SilcClientConnectCallback callback,
585 SilcClientConnection conn;
586 SilcFSMThread thread;
591 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
593 conn = silc_calloc(1, sizeof(*conn));
597 conn->client = client;
598 conn->public_key = public_key;
599 conn->private_key = private_key;
600 conn->remote_host = strdup(remote_host);
601 conn->remote_port = port ? port : 706;
602 conn->type = conn_type;
603 conn->callback = callback;
604 conn->callback_context = context;
606 conn->internal = silc_calloc(1, sizeof(*conn->internal));
607 if (!conn->internal) {
611 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
612 silc_mutex_alloc(&conn->internal->lock);
613 silc_atomic_init16(&conn->internal->cmd_ident, 0);
615 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
617 silc_free(conn->internal);
623 conn->internal->params = *params;
624 if (!conn->internal->params.rekey_secs)
625 conn->internal->params.rekey_secs = 3600;
626 #ifndef SILC_DIST_INPLACE
627 if (conn->internal->params.rekey_secs < 300)
628 conn->internal->params.rekey_secs = 300;
629 #endif /* SILC_DIST_INPLACE */
631 conn->internal->verbose = TRUE;
632 silc_list_init(conn->internal->pending_commands,
633 struct SilcClientCommandContextStruct, next);
634 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
636 /* Allocate client, channel and serve caches */
637 if (conn_type != SILC_CONN_CLIENT) {
638 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
640 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
642 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
644 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
645 !conn->internal->server_cache) {
646 silc_client_del_connection(client, conn);
652 /* Initialize our async operation so that application may abort us
653 while we're connecting. */
654 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
656 if (!conn->internal->cop) {
657 silc_client_del_connection(client, conn);
662 /* Run the connection state machine. If threads are in use the connection
663 machine is always run in a real thread. */
664 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
665 silc_client_connection_finished, NULL,
666 client->internal->params->threads);
668 silc_client_del_connection(client, conn);
671 silc_fsm_set_state_context(thread, client);
672 silc_fsm_start(thread, silc_client_connection_st_start);
674 SILC_LOG_DEBUG(("New connection %p", conn));
675 silc_atomic_add_int16(&client->internal->conns, 1);
680 /* Deletes connection. This is always called from the connection machine
681 destructor. Do not call this directly other places. */
683 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
686 SilcIDCacheEntry entry;
687 SilcFSMThread thread;
689 SILC_LOG_DEBUG(("Freeing connection %p", conn));
691 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
693 /* Free all cache entries */
694 if (conn->internal->server_cache) {
695 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
696 silc_list_start(list);
697 while ((entry = silc_list_get(list)))
698 silc_client_del_server(client, conn, entry->context);
701 if (conn->internal->channel_cache) {
702 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
703 silc_list_start(list);
704 while ((entry = silc_list_get(list))) {
705 silc_client_empty_channel(client, conn, entry->context);
706 silc_client_del_channel(client, conn, entry->context);
710 if (conn->internal->client_cache) {
711 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
712 silc_list_start(list);
713 while ((entry = silc_list_get(list)))
714 silc_client_del_client(client, conn, entry->context);
719 if (conn->internal->client_cache)
720 silc_idcache_free(conn->internal->client_cache);
721 if (conn->internal->channel_cache)
722 silc_idcache_free(conn->internal->channel_cache);
723 if (conn->internal->server_cache)
724 silc_idcache_free(conn->internal->server_cache);
726 /* Free thread pool */
727 silc_list_start(conn->internal->thread_pool);
728 while ((thread = silc_list_get(conn->internal->thread_pool)))
729 silc_fsm_free(thread);
731 silc_free(conn->remote_host);
732 silc_buffer_free(conn->internal->local_idp);
733 silc_buffer_free(conn->internal->remote_idp);
734 silc_mutex_free(conn->internal->lock);
735 if (conn->internal->hash)
736 silc_hash_free(conn->internal->hash);
737 if (conn->internal->sha1hash)
738 silc_hash_free(conn->internal->sha1hash);
739 silc_atomic_uninit16(&conn->internal->cmd_ident);
740 silc_free(conn->internal->away_message);
741 if (conn->internal->rekey)
742 silc_ske_free_rekey_material(conn->internal->rekey);
743 if (conn->internal->cop)
744 silc_async_free(conn->internal->cop);
746 silc_free(conn->internal);
747 memset(conn, 'F', sizeof(*conn));
751 /******************************* Client API *********************************/
753 /* Connects to remote server. This is the main routine used to connect
754 to remote SILC server. Performs key exchange also. Returns the
755 connection context to the connection callback. */
758 silc_client_connect_to_server(SilcClient client,
759 SilcClientConnectionParams *params,
760 SilcPublicKey public_key,
761 SilcPrivateKey private_key,
762 char *remote_host, int port,
763 SilcClientConnectCallback callback,
766 SilcClientConnection conn;
768 SILC_LOG_DEBUG(("Connecting to server"));
770 if (!client || !remote_host)
773 /* Add new connection */
774 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
775 public_key, private_key, remote_host,
776 port, callback, context);
778 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
782 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
783 "Connecting to port %d of server %s",
786 /* Signal connection machine to start connecting */
787 conn->internal->connect = TRUE;
788 return conn->internal->cop;
791 /* Connects to remote client. Performs key exchange also. Returns the
792 connection context to the connection callback. */
795 silc_client_connect_to_client(SilcClient client,
796 SilcClientConnectionParams *params,
797 SilcPublicKey public_key,
798 SilcPrivateKey private_key,
799 char *remote_host, int port,
800 SilcClientConnectCallback callback,
803 SilcClientConnection conn;
805 SILC_LOG_DEBUG(("Connecting to client"));
807 if (!client || !remote_host)
811 params->no_authentication = TRUE;
813 /* Add new connection */
814 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
815 public_key, private_key, remote_host,
816 port, callback, context);
818 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
822 /* Signal connection machine to start connecting */
823 conn->internal->connect = TRUE;
824 return conn->internal->cop;
827 /* Starts key exchange in the remote stream indicated by `stream'. This
828 creates the connection context and returns it in the connection callback. */
831 silc_client_key_exchange(SilcClient client,
832 SilcClientConnectionParams *params,
833 SilcPublicKey public_key,
834 SilcPrivateKey private_key,
836 SilcConnectionType conn_type,
837 SilcClientConnectCallback callback,
840 SilcClientConnection conn;
844 SILC_LOG_DEBUG(("Performing key exchange"));
846 if (!client || !stream)
849 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
850 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
851 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
855 /* Add new connection */
856 conn = silc_client_add_connection(client, conn_type, TRUE, params,
857 public_key, private_key,
858 (char *)host, port, callback, context);
860 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
863 conn->internal->user_stream = stream;
865 /* Signal connection to start key exchange */
866 conn->internal->key_exchange = TRUE;
867 return conn->internal->cop;
870 /* Closes remote connection */
872 void silc_client_close_connection(SilcClient client,
873 SilcClientConnection conn)
875 SILC_LOG_DEBUG(("Closing connection %p", conn));
877 /* Signal to close connection */
878 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
879 if (!conn->internal->disconnected) {
880 conn->internal->disconnected = TRUE;
881 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
885 /* Allocates new client object. This has to be done before client may
886 work. After calling this one must call silc_client_init to initialize
887 the client. The `application' is application specific user data pointer
888 and caller must free it. */
890 SilcClient silc_client_alloc(SilcClientOperations *ops,
891 SilcClientParams *params,
893 const char *version_string)
895 SilcClient new_client;
897 new_client = silc_calloc(1, sizeof(*new_client));
900 new_client->application = application;
902 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
903 if (!new_client->internal) {
904 silc_free(new_client);
907 new_client->internal->ops = ops;
908 new_client->internal->params =
909 silc_calloc(1, sizeof(*new_client->internal->params));
911 version_string = silc_version_string;
912 new_client->internal->silc_client_version = strdup(version_string);
915 memcpy(new_client->internal->params, params, sizeof(*params));
917 new_client->internal->params->
918 nickname_format[sizeof(new_client->internal->
919 params->nickname_format) - 1] = 0;
921 silc_atomic_init16(&new_client->internal->conns, 0);
926 /* Frees client object and its internals. */
928 void silc_client_free(SilcClient client)
930 silc_schedule_uninit(client->schedule);
933 silc_rng_free(client->rng);
935 if (!client->internal->params->dont_register_crypto_library) {
936 silc_cipher_unregister_all();
937 silc_pkcs_unregister_all();
938 silc_hash_unregister_all();
939 silc_hmac_unregister_all();
942 silc_packet_engine_stop(client->internal->packet_engine);
943 silc_dlist_uninit(client->internal->ftp_sessions);
944 silc_atomic_uninit16(&client->internal->conns);
945 silc_mutex_free(client->internal->lock);
946 silc_free(client->username);
947 silc_free(client->hostname);
948 silc_free(client->realname);
949 silc_free(client->internal->params);
950 silc_free(client->internal->silc_client_version);
951 silc_free(client->internal);
955 /* Initializes the client. This makes all the necessary steps to make
956 the client ready to be run. One must call silc_client_run to run the
957 client. Returns FALSE if error occured, TRUE otherwise. */
959 SilcBool silc_client_init(SilcClient client, const char *username,
960 const char *hostname, const char *realname,
961 SilcClientRunning running, void *context)
963 SILC_LOG_DEBUG(("Initializing client"));
968 if (!username || !hostname) {
969 SILC_LOG_ERROR(("Username and hostname must be given to "
970 "silc_client_init"));
976 /* Validate essential strings */
977 if (!silc_identifier_verify(username, strlen(username),
978 SILC_STRING_UTF8, 128)) {
979 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
983 if (!silc_identifier_verify(hostname, strlen(hostname),
984 SILC_STRING_UTF8, 256)) {
985 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
989 if (!silc_utf8_valid(realname, strlen(realname))) {
990 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
995 /* Take the name strings */
996 client->username = strdup(username);
997 client->hostname = strdup(hostname);
998 client->realname = strdup(realname);
999 if (!username || !hostname || !realname)
1002 client->internal->ftp_sessions = silc_dlist_init();
1003 if (!client->internal->ftp_sessions)
1006 if (!client->internal->params->dont_register_crypto_library) {
1007 /* Initialize the crypto library. If application has done this already
1008 this has no effect. Also, we will not be overriding something
1009 application might have registered earlier. */
1010 silc_cipher_register_default();
1011 silc_pkcs_register_default();
1012 silc_hash_register_default();
1013 silc_hmac_register_default();
1016 /* Initialize random number generator */
1017 client->rng = silc_rng_alloc();
1020 silc_rng_init(client->rng);
1021 silc_rng_global_init(client->rng);
1023 /* Initialize the scheduler */
1024 client->schedule = silc_schedule_init(0, client);
1025 if (!client->schedule)
1028 /* Allocate client lock */
1029 silc_mutex_alloc(&client->internal->lock);
1031 /* Register commands */
1032 silc_client_commands_register(client);
1034 /* Start packet engine */
1035 client->internal->packet_engine =
1036 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1038 if (!client->internal->packet_engine)
1041 /* Initialize and start the client FSM */
1042 client->internal->running = running;
1043 client->internal->running_context = context;
1044 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1045 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1046 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1048 /* Signal the application when we are running */
1049 client->internal->run_callback = TRUE;
1050 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1055 /* Starts the SILC client FSM machine and blocks here. When this returns
1056 the client has ended. */
1058 void silc_client_run(SilcClient client)
1060 SILC_LOG_DEBUG(("Starting SILC client"));
1062 /* Run the scheduler */
1063 silc_schedule(client->schedule);
1066 /* Call scheduler one iteration and return. */
1068 void silc_client_run_one(SilcClient client)
1070 if (silc_fsm_is_started(&client->internal->fsm))
1071 silc_schedule_one(client->schedule, 0);
1074 /* Stops the client. This is called to stop the client and thus to stop
1077 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1080 SILC_LOG_DEBUG(("Stopping client"));
1082 client->internal->running = (SilcClientRunning)stopped;
1083 client->internal->running_context = context;
1085 /* Signal to stop */
1086 client->internal->stop = TRUE;
1087 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);