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) {
519 /* Call running callbcak back to application */
520 client->internal->run_callback = FALSE;
521 if (client->internal->running) {
522 SILC_LOG_DEBUG(("We are up, call running callback"));
523 client->internal->running(client, client->internal->running_context);
525 return SILC_FSM_CONTINUE;
528 if (client->internal->connection_closed) {
529 /* A connection finished */
530 SILC_LOG_DEBUG(("Event: connection closed"));
531 client->internal->connection_closed = FALSE;
532 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
533 client->internal->stop)
534 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
535 return SILC_FSM_CONTINUE;
538 if (client->internal->stop) {
539 /* Stop client libarry. If we have running connections, wait until
540 they finish first. */
541 if (silc_atomic_get_int16(&client->internal->conns) == 0) {
542 SILC_LOG_DEBUG(("Event: stop"));
543 silc_fsm_next(fsm, silc_client_st_stop);
545 return SILC_FSM_CONTINUE;
550 return SILC_FSM_CONTINUE;
553 /* Stop event. Stops the client library. */
555 SILC_FSM_STATE(silc_client_st_stop)
557 SilcClient client = fsm_context;
559 SILC_LOG_DEBUG(("Client stopped"));
562 silc_schedule_stop(client->schedule);
563 silc_client_commands_unregister(client);
565 /* Call stopped callback to application */
566 if (client->internal->running)
567 client->internal->running(client, client->internal->running_context);
569 return SILC_FSM_FINISH;
572 /******************************* Private API ********************************/
574 /* Adds new connection. Creates the connection context and returns it. */
577 silc_client_add_connection(SilcClient client,
578 SilcConnectionType conn_type,
580 SilcClientConnectionParams *params,
581 SilcPublicKey public_key,
582 SilcPrivateKey private_key,
583 char *remote_host, int port,
584 SilcClientConnectCallback callback,
587 SilcClientConnection conn;
588 SilcFSMThread thread;
593 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
595 conn = silc_calloc(1, sizeof(*conn));
599 conn->client = client;
600 conn->public_key = public_key;
601 conn->private_key = private_key;
602 conn->remote_host = strdup(remote_host);
603 conn->remote_port = port ? port : 706;
604 conn->type = conn_type;
605 conn->callback = callback;
606 conn->callback_context = context;
608 conn->internal = silc_calloc(1, sizeof(*conn->internal));
609 if (!conn->internal) {
613 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
614 silc_mutex_alloc(&conn->internal->lock);
615 silc_atomic_init16(&conn->internal->cmd_ident, 0);
617 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
619 silc_free(conn->internal);
625 conn->internal->params = *params;
626 if (!conn->internal->params.rekey_secs)
627 conn->internal->params.rekey_secs = 3600;
628 #ifndef SILC_DIST_INPLACE
629 if (conn->internal->params.rekey_secs < 300)
630 conn->internal->params.rekey_secs = 300;
631 #endif /* SILC_DIST_INPLACE */
633 conn->internal->verbose = TRUE;
634 silc_list_init(conn->internal->pending_commands,
635 struct SilcClientCommandContextStruct, next);
636 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
638 /* Allocate client, channel and serve caches */
639 if (conn_type != SILC_CONN_CLIENT) {
640 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
642 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
644 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
646 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
647 !conn->internal->server_cache) {
648 silc_client_del_connection(client, conn);
654 /* Initialize our async operation so that application may abort us
655 while we're connecting. */
656 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
658 if (!conn->internal->cop) {
659 silc_client_del_connection(client, conn);
664 /* Run the connection state machine. If threads are in use the connection
665 machine is always run in a real thread. */
666 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
667 silc_client_connection_finished, NULL,
668 client->internal->params->threads);
670 silc_client_del_connection(client, conn);
673 silc_fsm_set_state_context(thread, client);
674 silc_fsm_start(thread, silc_client_connection_st_start);
676 SILC_LOG_DEBUG(("New connection %p", conn));
677 silc_atomic_add_int16(&client->internal->conns, 1);
682 /* Deletes connection. This is always called from the connection machine
683 destructor. Do not call this directly other places. */
685 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
688 SilcIDCacheEntry entry;
689 SilcFSMThread thread;
691 SILC_LOG_DEBUG(("Freeing connection %p", conn));
693 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
695 /* Free all cache entries */
696 if (conn->internal->server_cache) {
697 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
698 silc_list_start(list);
699 while ((entry = silc_list_get(list)))
700 silc_client_del_server(client, conn, entry->context);
703 if (conn->internal->channel_cache) {
704 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
705 silc_list_start(list);
706 while ((entry = silc_list_get(list))) {
707 silc_client_empty_channel(client, conn, entry->context);
708 silc_client_del_channel(client, conn, entry->context);
712 if (conn->internal->client_cache) {
713 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
714 silc_list_start(list);
715 while ((entry = silc_list_get(list)))
716 silc_client_del_client(client, conn, entry->context);
721 if (conn->internal->client_cache)
722 silc_idcache_free(conn->internal->client_cache);
723 if (conn->internal->channel_cache)
724 silc_idcache_free(conn->internal->channel_cache);
725 if (conn->internal->server_cache)
726 silc_idcache_free(conn->internal->server_cache);
728 /* Free thread pool */
729 silc_list_start(conn->internal->thread_pool);
730 while ((thread = silc_list_get(conn->internal->thread_pool)))
731 silc_fsm_free(thread);
733 silc_free(conn->remote_host);
734 silc_buffer_free(conn->internal->local_idp);
735 silc_buffer_free(conn->internal->remote_idp);
736 silc_mutex_free(conn->internal->lock);
737 if (conn->internal->hash)
738 silc_hash_free(conn->internal->hash);
739 if (conn->internal->sha1hash)
740 silc_hash_free(conn->internal->sha1hash);
741 silc_atomic_uninit16(&conn->internal->cmd_ident);
742 silc_free(conn->internal->away_message);
743 if (conn->internal->rekey)
744 silc_ske_free_rekey_material(conn->internal->rekey);
745 if (conn->internal->cop)
746 silc_async_free(conn->internal->cop);
748 silc_free(conn->internal);
749 memset(conn, 'F', sizeof(*conn));
753 /******************************* Client API *********************************/
755 /* Connects to remote server. This is the main routine used to connect
756 to remote SILC server. Performs key exchange also. Returns the
757 connection context to the connection callback. */
760 silc_client_connect_to_server(SilcClient client,
761 SilcClientConnectionParams *params,
762 SilcPublicKey public_key,
763 SilcPrivateKey private_key,
764 char *remote_host, int port,
765 SilcClientConnectCallback callback,
768 SilcClientConnection conn;
770 SILC_LOG_DEBUG(("Connecting to server"));
772 if (!client || !remote_host)
775 if (client->internal->run_callback) {
776 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
777 "callback has not been called yet."));
781 /* Add new connection */
782 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
783 public_key, private_key, remote_host,
784 port, callback, context);
786 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
790 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
791 "Connecting to port %d of server %s",
794 /* Signal connection machine to start connecting */
795 conn->internal->connect = TRUE;
796 return conn->internal->cop;
799 /* Connects to remote client. Performs key exchange also. Returns the
800 connection context to the connection callback. */
803 silc_client_connect_to_client(SilcClient client,
804 SilcClientConnectionParams *params,
805 SilcPublicKey public_key,
806 SilcPrivateKey private_key,
807 char *remote_host, int port,
808 SilcClientConnectCallback callback,
811 SilcClientConnection conn;
813 SILC_LOG_DEBUG(("Connecting to client"));
815 if (!client || !remote_host)
818 if (client->internal->run_callback) {
819 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
820 "callback has not been called yet."));
825 params->no_authentication = TRUE;
827 /* Add new connection */
828 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
829 public_key, private_key, remote_host,
830 port, callback, context);
832 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
836 /* Signal connection machine to start connecting */
837 conn->internal->connect = TRUE;
838 return conn->internal->cop;
841 /* Starts key exchange in the remote stream indicated by `stream'. This
842 creates the connection context and returns it in the connection callback. */
845 silc_client_key_exchange(SilcClient client,
846 SilcClientConnectionParams *params,
847 SilcPublicKey public_key,
848 SilcPrivateKey private_key,
850 SilcConnectionType conn_type,
851 SilcClientConnectCallback callback,
854 SilcClientConnection conn;
858 SILC_LOG_DEBUG(("Performing key exchange"));
860 if (!client || !stream)
863 if (client->internal->run_callback) {
864 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
865 "callback has not been called yet."));
869 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
870 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
871 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
875 /* Add new connection */
876 conn = silc_client_add_connection(client, conn_type, TRUE, params,
877 public_key, private_key,
878 (char *)host, port, callback, context);
880 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
883 conn->internal->user_stream = stream;
885 /* Signal connection to start key exchange */
886 conn->internal->key_exchange = TRUE;
887 return conn->internal->cop;
890 /* Closes remote connection */
892 void silc_client_close_connection(SilcClient client,
893 SilcClientConnection conn)
895 SILC_LOG_DEBUG(("Closing connection %p", conn));
897 /* Signal to close connection */
898 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
899 if (!conn->internal->disconnected) {
900 conn->internal->disconnected = TRUE;
901 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
905 /* Allocates new client object. This has to be done before client may
906 work. After calling this one must call silc_client_init to initialize
907 the client. The `application' is application specific user data pointer
908 and caller must free it. */
910 SilcClient silc_client_alloc(SilcClientOperations *ops,
911 SilcClientParams *params,
913 const char *version_string)
915 SilcClient new_client;
917 new_client = silc_calloc(1, sizeof(*new_client));
920 new_client->application = application;
922 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
923 if (!new_client->internal) {
924 silc_free(new_client);
927 new_client->internal->ops = ops;
928 new_client->internal->params =
929 silc_calloc(1, sizeof(*new_client->internal->params));
931 version_string = silc_version_string;
932 new_client->internal->silc_client_version = strdup(version_string);
935 memcpy(new_client->internal->params, params, sizeof(*params));
937 new_client->internal->params->
938 nickname_format[sizeof(new_client->internal->
939 params->nickname_format) - 1] = 0;
941 silc_atomic_init16(&new_client->internal->conns, 0);
946 /* Frees client object and its internals. */
948 void silc_client_free(SilcClient client)
950 silc_schedule_uninit(client->schedule);
953 silc_rng_free(client->rng);
955 if (!client->internal->params->dont_register_crypto_library) {
956 silc_cipher_unregister_all();
957 silc_pkcs_unregister_all();
958 silc_hash_unregister_all();
959 silc_hmac_unregister_all();
962 silc_packet_engine_stop(client->internal->packet_engine);
963 silc_dlist_uninit(client->internal->ftp_sessions);
964 silc_atomic_uninit16(&client->internal->conns);
965 silc_mutex_free(client->internal->lock);
966 silc_free(client->username);
967 silc_free(client->hostname);
968 silc_free(client->realname);
969 silc_free(client->internal->params);
970 silc_free(client->internal->silc_client_version);
971 silc_free(client->internal);
975 /* Initializes the client. This makes all the necessary steps to make
976 the client ready to be run. One must call silc_client_run to run the
977 client. Returns FALSE if error occured, TRUE otherwise. */
979 SilcBool silc_client_init(SilcClient client, const char *username,
980 const char *hostname, const char *realname,
981 SilcClientRunning running, void *context)
983 SILC_LOG_DEBUG(("Initializing client"));
988 if (!username || !hostname) {
989 SILC_LOG_ERROR(("Username and hostname must be given to "
990 "silc_client_init"));
996 /* Validate essential strings */
997 if (!silc_identifier_verify(username, strlen(username),
998 SILC_STRING_UTF8, 128)) {
999 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1003 if (!silc_identifier_verify(hostname, strlen(hostname),
1004 SILC_STRING_UTF8, 256)) {
1005 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1009 if (!silc_utf8_valid(realname, strlen(realname))) {
1010 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1015 /* Take the name strings */
1016 client->username = strdup(username);
1017 client->hostname = strdup(hostname);
1018 client->realname = strdup(realname);
1019 if (!username || !hostname || !realname)
1022 client->internal->ftp_sessions = silc_dlist_init();
1023 if (!client->internal->ftp_sessions)
1026 if (!client->internal->params->dont_register_crypto_library) {
1027 /* Initialize the crypto library. If application has done this already
1028 this has no effect. Also, we will not be overriding something
1029 application might have registered earlier. */
1030 silc_cipher_register_default();
1031 silc_pkcs_register_default();
1032 silc_hash_register_default();
1033 silc_hmac_register_default();
1036 /* Initialize random number generator */
1037 client->rng = silc_rng_alloc();
1040 silc_rng_init(client->rng);
1041 silc_rng_global_init(client->rng);
1043 /* Initialize the scheduler */
1044 client->schedule = silc_schedule_init(0, client, NULL);
1045 if (!client->schedule)
1048 /* Allocate client lock */
1049 silc_mutex_alloc(&client->internal->lock);
1051 /* Register commands */
1052 silc_client_commands_register(client);
1054 /* Start packet engine */
1055 client->internal->packet_engine =
1056 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1058 if (!client->internal->packet_engine)
1061 /* Initialize and start the client FSM */
1062 client->internal->running = running;
1063 client->internal->running_context = context;
1064 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1065 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1066 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1068 /* Signal the application when we are running */
1069 client->internal->run_callback = TRUE;
1070 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1075 /* Starts the SILC client FSM machine and blocks here. When this returns
1076 the client has ended. */
1078 void silc_client_run(SilcClient client)
1080 SILC_LOG_DEBUG(("Starting SILC client"));
1082 /* Run the scheduler */
1083 silc_schedule(client->schedule);
1086 /* Call scheduler one iteration and return. */
1088 void silc_client_run_one(SilcClient client)
1090 if (silc_fsm_is_started(&client->internal->fsm))
1091 silc_schedule_one(client->schedule, 0);
1094 /* Stops the client. This is called to stop the client and thus to stop
1097 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1100 SILC_LOG_DEBUG(("Stopping client"));
1102 if (!silc_fsm_is_started(&client->internal->fsm)) {
1103 SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1104 "started (silc_client_init not called)"));
1108 client->internal->running = (SilcClientRunning)stopped;
1109 client->internal->running_context = context;
1111 /* Signal to stop */
1112 client->internal->stop = TRUE;
1113 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);