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 SilcIDCacheEntry entry;
396 SilcClientEntry client_entry;
398 /* Finish running command threads. This will also finish waiting packet
399 thread, as they are always waiting for some command. If any thread is
400 waiting something else than command, they must be finished explicitly. */
401 if (silc_list_count(conn->internal->pending_commands)) {
402 SILC_LOG_DEBUG(("Finish pending commands"));
403 silc_list_start(conn->internal->pending_commands);
404 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
405 if (silc_fsm_is_started(&cmd->thread)) {
406 cmd->verbose = FALSE;
407 silc_fsm_continue_sync(&cmd->thread);
411 /* Give threads time to finish */
412 return SILC_FSM_YIELD;
415 /* Abort ongoing event */
416 if (conn->internal->op) {
417 SILC_LOG_DEBUG(("Abort event"));
418 silc_async_abort(conn->internal->op, NULL, NULL);
419 conn->internal->op = NULL;
422 /* Abort ongoing client entry operations */
423 if (conn->internal->client_cache) {
424 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
425 silc_list_start(list);
426 while ((entry = silc_list_get(list))) {
427 client_entry = entry->context;
428 if (client_entry->internal.op) {
429 silc_async_abort(client_entry->internal.op, NULL, NULL);
430 client_entry->internal.op = NULL;
436 /* If event thread is running, finish it. */
437 if (silc_fsm_is_started(&conn->internal->event_thread)) {
438 SILC_LOG_DEBUG(("Finish event thread"));
439 silc_fsm_continue_sync(&conn->internal->event_thread);
440 return SILC_FSM_YIELD;
443 /* Call the connection callback */
445 conn->callback(conn->client, conn, conn->internal->status,
446 conn->internal->error, conn->internal->disconnect_message,
447 conn->callback_context);
448 silc_free(conn->internal->disconnect_message);
450 SILC_LOG_DEBUG(("Closing remote connection"));
452 /* Close connection. */
454 silc_packet_stream_destroy(conn->stream);
456 SILC_LOG_DEBUG(("Finishing connection machine"));
457 return SILC_FSM_FINISH;
460 /* Received error packet from server. Send it to application. */
462 SILC_FSM_STATE(silc_client_error)
464 SilcClientConnection conn = fsm_context;
465 SilcClient client = conn->client;
466 SilcPacket packet = state_context;
469 msg = silc_memdup(silc_buffer_data(&packet->buffer),
470 silc_buffer_len(&packet->buffer));
472 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, msg);
475 silc_packet_free(packet);
477 return SILC_FSM_FINISH;
480 /* Received disconnect packet from server. We close the connection and
481 send the disconnect message to application. */
483 SILC_FSM_STATE(silc_client_disconnect)
485 SilcClientConnection conn = fsm_context;
486 SilcPacket packet = state_context;
488 char *message = NULL;
490 SILC_LOG_DEBUG(("Server disconnected"));
492 if (silc_buffer_len(&packet->buffer) < 1) {
493 silc_packet_free(packet);
494 return SILC_FSM_FINISH;
497 status = (SilcStatus)packet->buffer.data[0];
499 silc_buffer_pull(&packet->buffer, 1);
500 if (silc_buffer_len(&packet->buffer) > 1 &&
501 silc_utf8_valid(silc_buffer_data(&packet->buffer),
502 silc_buffer_len(&packet->buffer)))
503 message = silc_memdup(silc_buffer_data(&packet->buffer),
504 silc_buffer_len(&packet->buffer));
506 /* Call connection callback */
507 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
508 conn->internal->error = status;
509 conn->internal->disconnect_message = message;
511 /* Signal to close connection */
512 if (!conn->internal->disconnected) {
513 conn->internal->disconnected = TRUE;
514 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
517 silc_packet_free(packet);
519 return SILC_FSM_FINISH;
522 /*************************** Main client machine ****************************/
524 /* The client's main state where we wait for various events */
526 SILC_FSM_STATE(silc_client_st_run)
528 SilcClient client = fsm_context;
530 /* Wait for events */
531 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
535 if (client->internal->run_callback) {
536 /* Call running callbcak back to application */
537 client->internal->run_callback = FALSE;
538 if (client->internal->running) {
539 SILC_LOG_DEBUG(("We are up, call running callback"));
540 client->internal->running(client, client->internal->running_context);
542 return SILC_FSM_CONTINUE;
545 if (client->internal->connection_closed) {
546 /* A connection finished */
547 SILC_LOG_DEBUG(("Event: connection closed"));
548 client->internal->connection_closed = FALSE;
549 if (silc_atomic_get_int32(&client->internal->conns) == 0 &&
550 client->internal->stop)
551 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
552 return SILC_FSM_CONTINUE;
555 if (client->internal->stop) {
556 /* Stop client libarry. If we have running connections, wait until
557 they finish first. */
558 if (silc_atomic_get_int32(&client->internal->conns) == 0) {
559 SILC_LOG_DEBUG(("Event: stop"));
560 silc_fsm_next(fsm, silc_client_st_stop);
562 return SILC_FSM_CONTINUE;
567 return SILC_FSM_CONTINUE;
570 /* Stop event. Stops the client library. */
572 SILC_FSM_STATE(silc_client_st_stop)
574 SilcClient client = fsm_context;
576 SILC_LOG_DEBUG(("Client stopped"));
579 silc_schedule_stop(client->schedule);
580 silc_client_commands_unregister(client);
582 /* Call stopped callback to application */
583 if (client->internal->running)
584 client->internal->running(client, client->internal->running_context);
586 return SILC_FSM_FINISH;
589 /******************************* Private API ********************************/
591 /* Adds new connection. Creates the connection context and returns it. */
594 silc_client_add_connection(SilcClient client,
595 SilcConnectionType conn_type,
597 SilcClientConnectionParams *params,
598 SilcPublicKey public_key,
599 SilcPrivateKey private_key,
600 char *remote_host, int port,
601 SilcClientConnectCallback callback,
604 SilcClientConnection conn;
605 SilcFSMThread thread;
610 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
612 conn = silc_calloc(1, sizeof(*conn));
616 conn->client = client;
617 conn->public_key = public_key;
618 conn->private_key = private_key;
619 conn->remote_host = strdup(remote_host);
620 conn->remote_port = port ? port : 706;
621 conn->type = conn_type;
622 conn->callback = callback;
623 conn->callback_context = context;
625 conn->internal = silc_calloc(1, sizeof(*conn->internal));
626 if (!conn->internal) {
630 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
631 silc_mutex_alloc(&conn->internal->lock);
632 silc_atomic_init16(&conn->internal->cmd_ident, 0);
634 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
636 silc_free(conn->internal);
642 conn->internal->params = *params;
643 conn->context = params->context;
645 if (!conn->internal->params.rekey_secs)
646 conn->internal->params.rekey_secs = 3600;
647 #ifndef SILC_DIST_INPLACE
648 if (conn->internal->params.rekey_secs < 300)
649 conn->internal->params.rekey_secs = 300;
650 #endif /* SILC_DIST_INPLACE */
652 conn->internal->verbose = TRUE;
653 silc_list_init(conn->internal->pending_commands,
654 struct SilcClientCommandContextStruct, next);
655 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
657 /* Allocate client, channel and serve caches */
658 if (conn_type != SILC_CONN_CLIENT) {
659 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
661 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
663 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
665 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
666 !conn->internal->server_cache) {
667 silc_client_del_connection(client, conn);
673 /* Initialize our async operation so that application may abort us
674 while we're connecting. */
675 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
677 if (!conn->internal->cop) {
678 silc_client_del_connection(client, conn);
683 /* Run the connection state machine. If threads are in use the connection
684 machine is always run in a real thread. */
685 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
686 silc_client_connection_finished, NULL,
687 client->internal->params->threads);
689 silc_client_del_connection(client, conn);
692 silc_fsm_set_state_context(thread, client);
693 silc_fsm_start(thread, silc_client_connection_st_start);
695 SILC_LOG_DEBUG(("New connection %p", conn));
696 silc_atomic_add_int32(&client->internal->conns, 1);
701 /* Deletes connection. This is always called from the connection machine
702 destructor. Do not call this directly other places. */
704 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
707 SilcIDCacheEntry entry;
708 SilcFSMThread thread;
710 SILC_LOG_DEBUG(("Freeing connection %p", conn));
712 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
714 /* Free all cache entries */
715 if (conn->internal->server_cache) {
716 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
717 silc_list_start(list);
718 while ((entry = silc_list_get(list)))
719 silc_client_del_server(client, conn, entry->context);
722 if (conn->internal->channel_cache) {
723 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
724 silc_list_start(list);
725 while ((entry = silc_list_get(list))) {
726 silc_client_empty_channel(client, conn, entry->context);
727 silc_client_del_channel(client, conn, entry->context);
731 if (conn->internal->client_cache) {
732 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
733 silc_list_start(list);
734 while ((entry = silc_list_get(list)))
735 silc_client_del_client(client, conn, entry->context);
740 if (conn->internal->client_cache)
741 silc_idcache_free(conn->internal->client_cache);
742 if (conn->internal->channel_cache)
743 silc_idcache_free(conn->internal->channel_cache);
744 if (conn->internal->server_cache)
745 silc_idcache_free(conn->internal->server_cache);
747 /* Free thread pool */
748 silc_list_start(conn->internal->thread_pool);
749 while ((thread = silc_list_get(conn->internal->thread_pool)))
750 silc_fsm_free(thread);
752 silc_free(conn->remote_host);
753 silc_buffer_free(conn->internal->local_idp);
754 silc_buffer_free(conn->internal->remote_idp);
755 silc_mutex_free(conn->internal->lock);
756 if (conn->internal->hash)
757 silc_hash_free(conn->internal->hash);
758 if (conn->internal->sha1hash)
759 silc_hash_free(conn->internal->sha1hash);
760 silc_atomic_uninit16(&conn->internal->cmd_ident);
761 silc_free(conn->internal->away_message);
762 if (conn->internal->rekey)
763 silc_ske_free_rekey_material(conn->internal->rekey);
764 if (conn->internal->cop)
765 silc_async_free(conn->internal->cop);
767 silc_free(conn->internal);
768 memset(conn, 'F', sizeof(*conn));
772 /******************************* Client API *********************************/
774 /* Connects to remote server. This is the main routine used to connect
775 to remote SILC server. Performs key exchange also. Returns the
776 connection context to the connection callback. */
779 silc_client_connect_to_server(SilcClient client,
780 SilcClientConnectionParams *params,
781 SilcPublicKey public_key,
782 SilcPrivateKey private_key,
783 char *remote_host, int port,
784 SilcClientConnectCallback callback,
787 SilcClientConnection conn;
789 SILC_LOG_DEBUG(("Connecting to server"));
791 if (!client || !remote_host || !callback)
794 if (client->internal->run_callback) {
795 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
796 "callback has not been called yet."));
800 /* Add new connection */
801 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
802 public_key, private_key, remote_host,
803 port, callback, context);
805 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
809 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
810 "Connecting to port %d of server %s",
813 /* Signal connection machine to start connecting */
814 conn->internal->connect = TRUE;
815 return conn->internal->cop;
818 /* Connects to remote client. Performs key exchange also. Returns the
819 connection context to the connection callback. */
822 silc_client_connect_to_client(SilcClient client,
823 SilcClientConnectionParams *params,
824 SilcPublicKey public_key,
825 SilcPrivateKey private_key,
826 char *remote_host, int port,
827 SilcClientConnectCallback callback,
830 SilcClientConnection conn;
832 SILC_LOG_DEBUG(("Connecting to client"));
834 if (!client || !remote_host || !callback)
837 if (client->internal->run_callback) {
838 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
839 "callback has not been called yet."));
844 params->no_authentication = TRUE;
846 /* Add new connection */
847 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
848 public_key, private_key, remote_host,
849 port, callback, context);
851 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
855 /* Signal connection machine to start connecting */
856 conn->internal->connect = TRUE;
857 return conn->internal->cop;
860 /* Starts key exchange in the remote stream indicated by `stream'. This
861 creates the connection context and returns it in the connection callback. */
864 silc_client_key_exchange(SilcClient client,
865 SilcClientConnectionParams *params,
866 SilcPublicKey public_key,
867 SilcPrivateKey private_key,
869 SilcConnectionType conn_type,
870 SilcClientConnectCallback callback,
873 SilcClientConnection conn;
877 SILC_LOG_DEBUG(("Performing key exchange"));
879 if (!client || !stream || !callback)
882 if (client->internal->run_callback) {
883 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
884 "callback has not been called yet."));
888 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
889 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
890 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
894 /* Add new connection */
895 conn = silc_client_add_connection(client, conn_type, TRUE, params,
896 public_key, private_key,
897 (char *)host, port, callback, context);
899 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
902 conn->internal->user_stream = stream;
904 /* Signal connection to start key exchange */
905 conn->internal->key_exchange = TRUE;
906 return conn->internal->cop;
909 /* Closes remote connection */
911 void silc_client_close_connection(SilcClient client,
912 SilcClientConnection conn)
914 SILC_LOG_DEBUG(("Closing connection %p", conn));
916 /* Signal to close connection */
917 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
918 if (!conn->internal->disconnected) {
919 conn->internal->disconnected = TRUE;
920 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
924 /* Allocates new client object. This has to be done before client may
925 work. After calling this one must call silc_client_init to initialize
926 the client. The `application' is application specific user data pointer
927 and caller must free it. */
929 SilcClient silc_client_alloc(SilcClientOperations *ops,
930 SilcClientParams *params,
932 const char *version_string)
934 SilcClient new_client;
936 new_client = silc_calloc(1, sizeof(*new_client));
939 new_client->application = application;
941 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
942 if (!new_client->internal) {
943 silc_free(new_client);
946 new_client->internal->ops = ops;
947 new_client->internal->params =
948 silc_calloc(1, sizeof(*new_client->internal->params));
950 version_string = silc_version_string;
951 new_client->internal->silc_client_version = strdup(version_string);
954 memcpy(new_client->internal->params, params, sizeof(*params));
956 new_client->internal->params->
957 nickname_format[sizeof(new_client->internal->
958 params->nickname_format) - 1] = 0;
960 silc_atomic_init32(&new_client->internal->conns, 0);
965 /* Frees client object and its internals. */
967 void silc_client_free(SilcClient client)
969 if (client->schedule)
970 silc_schedule_uninit(client->schedule);
973 silc_rng_free(client->rng);
975 if (!client->internal->params->dont_register_crypto_library) {
976 silc_cipher_unregister_all();
977 silc_pkcs_unregister_all();
978 silc_hash_unregister_all();
979 silc_hmac_unregister_all();
982 if (client->internal->packet_engine)
983 silc_packet_engine_stop(client->internal->packet_engine);
984 if (client->internal->ftp_sessions)
985 silc_dlist_uninit(client->internal->ftp_sessions);
986 if (client->internal->lock)
987 silc_mutex_free(client->internal->lock);
988 silc_atomic_uninit32(&client->internal->conns);
989 silc_free(client->username);
990 silc_free(client->hostname);
991 silc_free(client->realname);
992 silc_free(client->internal->params);
993 silc_free(client->internal->silc_client_version);
994 silc_free(client->internal);
998 /* Initializes the client. This makes all the necessary steps to make
999 the client ready to be run. One must call silc_client_run to run the
1000 client. Returns FALSE if error occured, TRUE otherwise. */
1002 SilcBool silc_client_init(SilcClient client, const char *username,
1003 const char *hostname, const char *realname,
1004 SilcClientRunning running, void *context)
1006 SILC_LOG_DEBUG(("Initializing client"));
1011 if (!username || !hostname) {
1012 SILC_LOG_ERROR(("Username and hostname must be given to "
1013 "silc_client_init"));
1017 realname = username;
1019 /* Validate essential strings */
1020 if (!silc_identifier_verify(username, strlen(username),
1021 SILC_STRING_UTF8, 128)) {
1022 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1026 if (!silc_identifier_verify(hostname, strlen(hostname),
1027 SILC_STRING_UTF8, 256)) {
1028 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1032 if (!silc_utf8_valid(realname, strlen(realname))) {
1033 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1038 /* Take the name strings */
1039 client->username = strdup(username);
1040 client->hostname = strdup(hostname);
1041 client->realname = strdup(realname);
1042 if (!username || !hostname || !realname)
1045 client->internal->ftp_sessions = silc_dlist_init();
1046 if (!client->internal->ftp_sessions)
1049 if (!client->internal->params->dont_register_crypto_library) {
1050 /* Initialize the crypto library. If application has done this already
1051 this has no effect. Also, we will not be overriding something
1052 application might have registered earlier. */
1053 silc_cipher_register_default();
1054 silc_pkcs_register_default();
1055 silc_hash_register_default();
1056 silc_hmac_register_default();
1059 /* Initialize random number generator */
1060 client->rng = silc_rng_alloc();
1063 silc_rng_init(client->rng);
1064 silc_rng_global_init(client->rng);
1066 /* Initialize the scheduler */
1067 client->schedule = silc_schedule_init(0, client);
1068 if (!client->schedule)
1071 /* Allocate client lock */
1072 silc_mutex_alloc(&client->internal->lock);
1074 /* Register commands */
1075 silc_client_commands_register(client);
1077 /* Start packet engine */
1078 client->internal->packet_engine =
1079 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1081 if (!client->internal->packet_engine)
1084 /* Initialize and start the client FSM */
1085 client->internal->running = running;
1086 client->internal->running_context = context;
1087 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1088 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1089 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1091 /* Signal the application when we are running */
1092 client->internal->run_callback = TRUE;
1093 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1098 /* Starts the SILC client FSM machine and blocks here. When this returns
1099 the client has ended. */
1101 void silc_client_run(SilcClient client)
1103 SILC_LOG_DEBUG(("Starting SILC client"));
1105 /* Run the scheduler */
1106 silc_schedule(client->schedule);
1109 /* Call scheduler one iteration and return. */
1111 void silc_client_run_one(SilcClient client)
1113 if (silc_fsm_is_started(&client->internal->fsm))
1114 silc_schedule_one(client->schedule, 0);
1117 /* Stops the client. This is called to stop the client and thus to stop
1120 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1123 SILC_LOG_DEBUG(("Stopping client"));
1125 if (!silc_fsm_is_started(&client->internal->fsm)) {
1126 SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1127 "started (silc_client_init not called)"));
1131 client->internal->running = (SilcClientRunning)stopped;
1132 client->internal->running_context = context;
1134 /* Signal to stop */
1135 client->internal->stop = TRUE;
1136 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);