5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2014 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_int32(&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_ERROR, 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_int32(&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_int32(&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 conn->context = params->context;
628 if (!conn->internal->params.rekey_secs)
629 conn->internal->params.rekey_secs = 3600;
630 #ifndef SILC_DIST_INPLACE
631 if (conn->internal->params.rekey_secs < 300)
632 conn->internal->params.rekey_secs = 300;
633 #endif /* SILC_DIST_INPLACE */
635 conn->internal->verbose = TRUE;
636 silc_list_init(conn->internal->pending_commands,
637 struct SilcClientCommandContextStruct, next);
638 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
640 /* Allocate client, channel and serve caches */
641 if (conn_type != SILC_CONN_CLIENT) {
642 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
644 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
646 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
648 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
649 !conn->internal->server_cache) {
650 silc_client_del_connection(client, conn);
656 /* Initialize our async operation so that application may abort us
657 while we're connecting. */
658 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
660 if (!conn->internal->cop) {
661 silc_client_del_connection(client, conn);
666 /* Run the connection state machine. If threads are in use the connection
667 machine is always run in a real thread. */
668 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
669 silc_client_connection_finished, NULL,
670 client->internal->params->threads);
672 silc_client_del_connection(client, conn);
675 silc_fsm_set_state_context(thread, client);
676 silc_fsm_start(thread, silc_client_connection_st_start);
678 SILC_LOG_DEBUG(("New connection %p", conn));
679 silc_atomic_add_int32(&client->internal->conns, 1);
684 /* Deletes connection. This is always called from the connection machine
685 destructor. Do not call this directly other places. */
687 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
690 SilcIDCacheEntry entry;
691 SilcFSMThread thread;
693 SILC_LOG_DEBUG(("Freeing connection %p", conn));
695 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
697 /* Free all cache entries */
698 if (conn->internal->server_cache) {
699 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
700 silc_list_start(list);
701 while ((entry = silc_list_get(list)))
702 silc_client_del_server(client, conn, entry->context);
705 if (conn->internal->channel_cache) {
706 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
707 silc_list_start(list);
708 while ((entry = silc_list_get(list))) {
709 silc_client_empty_channel(client, conn, entry->context);
710 silc_client_del_channel(client, conn, entry->context);
714 if (conn->internal->client_cache) {
715 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
716 silc_list_start(list);
717 while ((entry = silc_list_get(list)))
718 silc_client_del_client(client, conn, entry->context);
723 if (conn->internal->client_cache)
724 silc_idcache_free(conn->internal->client_cache);
725 if (conn->internal->channel_cache)
726 silc_idcache_free(conn->internal->channel_cache);
727 if (conn->internal->server_cache)
728 silc_idcache_free(conn->internal->server_cache);
730 /* Free thread pool */
731 silc_list_start(conn->internal->thread_pool);
732 while ((thread = silc_list_get(conn->internal->thread_pool)))
733 silc_fsm_free(thread);
735 silc_free(conn->remote_host);
736 silc_buffer_free(conn->internal->local_idp);
737 silc_buffer_free(conn->internal->remote_idp);
738 silc_mutex_free(conn->internal->lock);
739 if (conn->internal->hash)
740 silc_hash_free(conn->internal->hash);
741 if (conn->internal->sha1hash)
742 silc_hash_free(conn->internal->sha1hash);
743 silc_atomic_uninit16(&conn->internal->cmd_ident);
744 silc_free(conn->internal->away_message);
745 if (conn->internal->rekey)
746 silc_ske_free_rekey_material(conn->internal->rekey);
747 if (conn->internal->cop)
748 silc_async_free(conn->internal->cop);
750 silc_free(conn->internal);
751 memset(conn, 'F', sizeof(*conn));
755 /******************************* Client API *********************************/
757 /* Connects to remote server. This is the main routine used to connect
758 to remote SILC server. Performs key exchange also. Returns the
759 connection context to the connection callback. */
762 silc_client_connect_to_server(SilcClient client,
763 SilcClientConnectionParams *params,
764 SilcPublicKey public_key,
765 SilcPrivateKey private_key,
766 char *remote_host, int port,
767 SilcClientConnectCallback callback,
770 SilcClientConnection conn;
772 SILC_LOG_DEBUG(("Connecting to server"));
774 if (!client || !remote_host || !callback)
777 if (client->internal->run_callback) {
778 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
779 "callback has not been called yet."));
783 /* Add new connection */
784 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
785 public_key, private_key, remote_host,
786 port, callback, context);
788 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
792 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
793 "Connecting to port %d of server %s",
796 /* Signal connection machine to start connecting */
797 conn->internal->connect = TRUE;
798 return conn->internal->cop;
801 /* Connects to remote client. Performs key exchange also. Returns the
802 connection context to the connection callback. */
805 silc_client_connect_to_client(SilcClient client,
806 SilcClientConnectionParams *params,
807 SilcPublicKey public_key,
808 SilcPrivateKey private_key,
809 char *remote_host, int port,
810 SilcClientConnectCallback callback,
813 SilcClientConnection conn;
815 SILC_LOG_DEBUG(("Connecting to client"));
817 if (!client || !remote_host || !callback)
820 if (client->internal->run_callback) {
821 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
822 "callback has not been called yet."));
827 params->no_authentication = TRUE;
829 /* Add new connection */
830 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
831 public_key, private_key, remote_host,
832 port, callback, context);
834 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
838 /* Signal connection machine to start connecting */
839 conn->internal->connect = TRUE;
840 return conn->internal->cop;
843 /* Starts key exchange in the remote stream indicated by `stream'. This
844 creates the connection context and returns it in the connection callback. */
847 silc_client_key_exchange(SilcClient client,
848 SilcClientConnectionParams *params,
849 SilcPublicKey public_key,
850 SilcPrivateKey private_key,
852 SilcConnectionType conn_type,
853 SilcClientConnectCallback callback,
856 SilcClientConnection conn;
860 SILC_LOG_DEBUG(("Performing key exchange"));
862 if (!client || !stream || !callback)
865 if (client->internal->run_callback) {
866 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
867 "callback has not been called yet."));
871 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
872 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
873 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
877 /* Add new connection */
878 conn = silc_client_add_connection(client, conn_type, TRUE, params,
879 public_key, private_key,
880 (char *)host, port, callback, context);
882 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
885 conn->internal->user_stream = stream;
887 /* Signal connection to start key exchange */
888 conn->internal->key_exchange = TRUE;
889 return conn->internal->cop;
892 /* Closes remote connection */
894 void silc_client_close_connection(SilcClient client,
895 SilcClientConnection conn)
897 SILC_LOG_DEBUG(("Closing connection %p", conn));
899 /* Signal to close connection */
900 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
901 if (!conn->internal->disconnected) {
902 conn->internal->disconnected = TRUE;
903 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
907 /* Allocates new client object. This has to be done before client may
908 work. After calling this one must call silc_client_init to initialize
909 the client. The `application' is application specific user data pointer
910 and caller must free it. */
912 SilcClient silc_client_alloc(SilcClientOperations *ops,
913 SilcClientParams *params,
915 const char *version_string)
917 SilcClient new_client;
919 new_client = silc_calloc(1, sizeof(*new_client));
922 new_client->application = application;
924 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
925 if (!new_client->internal) {
926 silc_free(new_client);
929 new_client->internal->ops = ops;
930 new_client->internal->params =
931 silc_calloc(1, sizeof(*new_client->internal->params));
933 version_string = silc_version_string;
934 new_client->internal->silc_client_version = strdup(version_string);
937 memcpy(new_client->internal->params, params, sizeof(*params));
939 new_client->internal->params->
940 nickname_format[sizeof(new_client->internal->
941 params->nickname_format) - 1] = 0;
943 silc_atomic_init32(&new_client->internal->conns, 0);
948 /* Frees client object and its internals. */
950 void silc_client_free(SilcClient client)
952 if (client->schedule)
953 silc_schedule_uninit(client->schedule);
956 silc_rng_free(client->rng);
958 if (!client->internal->params->dont_register_crypto_library) {
959 silc_cipher_unregister_all();
960 silc_pkcs_unregister_all();
961 silc_hash_unregister_all();
962 silc_hmac_unregister_all();
965 if (client->internal->packet_engine)
966 silc_packet_engine_stop(client->internal->packet_engine);
967 if (client->internal->ftp_sessions)
968 silc_dlist_uninit(client->internal->ftp_sessions);
969 if (client->internal->lock)
970 silc_mutex_free(client->internal->lock);
971 silc_atomic_uninit32(&client->internal->conns);
972 silc_free(client->username);
973 silc_free(client->hostname);
974 silc_free(client->realname);
975 silc_free(client->internal->params);
976 silc_free(client->internal->silc_client_version);
977 silc_free(client->internal);
981 /* Initializes the client. This makes all the necessary steps to make
982 the client ready to be run. One must call silc_client_run to run the
983 client. Returns FALSE if error occured, TRUE otherwise. */
985 SilcBool silc_client_init(SilcClient client, const char *username,
986 const char *hostname, const char *realname,
987 SilcClientRunning running, void *context)
989 SILC_LOG_DEBUG(("Initializing client"));
994 if (!username || !hostname) {
995 SILC_LOG_ERROR(("Username and hostname must be given to "
996 "silc_client_init"));
1000 realname = username;
1002 /* Validate essential strings */
1003 if (!silc_identifier_verify(username, strlen(username),
1004 SILC_STRING_UTF8, 128)) {
1005 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1009 if (!silc_identifier_verify(hostname, strlen(hostname),
1010 SILC_STRING_UTF8, 256)) {
1011 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1015 if (!silc_utf8_valid(realname, strlen(realname))) {
1016 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1021 /* Take the name strings */
1022 client->username = strdup(username);
1023 client->hostname = strdup(hostname);
1024 client->realname = strdup(realname);
1025 if (!username || !hostname || !realname)
1028 client->internal->ftp_sessions = silc_dlist_init();
1029 if (!client->internal->ftp_sessions)
1032 if (!client->internal->params->dont_register_crypto_library) {
1033 /* Initialize the crypto library. If application has done this already
1034 this has no effect. Also, we will not be overriding something
1035 application might have registered earlier. */
1036 silc_cipher_register_default();
1037 silc_pkcs_register_default();
1038 silc_hash_register_default();
1039 silc_hmac_register_default();
1042 /* Initialize random number generator */
1043 client->rng = silc_rng_alloc();
1046 silc_rng_init(client->rng);
1047 silc_rng_global_init(client->rng);
1049 /* Initialize the scheduler */
1050 client->schedule = silc_schedule_init(0, client);
1051 if (!client->schedule)
1054 /* Allocate client lock */
1055 silc_mutex_alloc(&client->internal->lock);
1057 /* Register commands */
1058 silc_client_commands_register(client);
1060 /* Start packet engine */
1061 client->internal->packet_engine =
1062 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1064 if (!client->internal->packet_engine)
1067 /* Initialize and start the client FSM */
1068 client->internal->running = running;
1069 client->internal->running_context = context;
1070 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1071 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1072 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1074 /* Signal the application when we are running */
1075 client->internal->run_callback = TRUE;
1076 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1081 /* Starts the SILC client FSM machine and blocks here. When this returns
1082 the client has ended. */
1084 void silc_client_run(SilcClient client)
1086 SILC_LOG_DEBUG(("Starting SILC client"));
1088 /* Run the scheduler */
1089 silc_schedule(client->schedule);
1092 /* Call scheduler one iteration and return. */
1094 void silc_client_run_one(SilcClient client)
1096 if (silc_fsm_is_started(&client->internal->fsm))
1097 silc_schedule_one(client->schedule, 0);
1100 /* Stops the client. This is called to stop the client and thus to stop
1103 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1106 SILC_LOG_DEBUG(("Stopping client"));
1108 if (!silc_fsm_is_started(&client->internal->fsm)) {
1109 SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1110 "started (silc_client_init not called)"));
1114 client->internal->running = (SilcClientRunning)stopped;
1115 client->internal->running_context = context;
1117 /* Signal to stop */
1118 client->internal->stop = TRUE;
1119 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);