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;
607 if (!client || !callback || !remote_host)
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) {
627 silc_free(conn->remote_host);
631 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
632 silc_mutex_alloc(&conn->internal->lock);
633 silc_atomic_init16(&conn->internal->cmd_ident, 0);
635 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
636 silc_free(conn->remote_host);
637 silc_free(conn->internal);
644 conn->internal->params = *params;
645 conn->context = params->context;
647 if (!conn->internal->params.rekey_secs)
648 conn->internal->params.rekey_secs = 3600;
649 #ifndef SILC_DIST_INPLACE
650 if (conn->internal->params.rekey_secs < 300)
651 conn->internal->params.rekey_secs = 300;
652 #endif /* SILC_DIST_INPLACE */
654 conn->internal->verbose = TRUE;
655 silc_list_init(conn->internal->pending_commands,
656 struct SilcClientCommandContextStruct, next);
657 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
659 /* Allocate client, channel and serve caches */
660 if (conn_type != SILC_CONN_CLIENT) {
661 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
663 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
665 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
667 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
668 !conn->internal->server_cache) {
669 silc_client_del_connection(client, conn);
675 /* Initialize our async operation so that application may abort us
676 while we're connecting. */
677 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
679 if (!conn->internal->cop) {
680 silc_client_del_connection(client, conn);
685 /* Run the connection state machine. If threads are in use the connection
686 machine is always run in a real thread. */
687 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
688 silc_client_connection_finished, NULL,
689 client->internal->params->threads);
691 silc_client_del_connection(client, conn);
694 silc_fsm_set_state_context(thread, client);
695 silc_fsm_start(thread, silc_client_connection_st_start);
697 SILC_LOG_DEBUG(("New connection %p", conn));
698 silc_atomic_add_int32(&client->internal->conns, 1);
703 /* Deletes connection. This is always called from the connection machine
704 destructor. Do not call this directly other places. */
706 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
709 SilcIDCacheEntry entry;
710 SilcFSMThread thread;
712 SILC_LOG_DEBUG(("Freeing connection %p", conn));
714 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
716 /* Free all cache entries */
717 if (conn->internal->server_cache) {
718 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
719 silc_list_start(list);
720 while ((entry = silc_list_get(list)))
721 silc_client_del_server(client, conn, entry->context);
724 if (conn->internal->channel_cache) {
725 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
726 silc_list_start(list);
727 while ((entry = silc_list_get(list))) {
728 silc_client_empty_channel(client, conn, entry->context);
729 silc_client_del_channel(client, conn, entry->context);
733 if (conn->internal->client_cache) {
734 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
735 silc_list_start(list);
736 while ((entry = silc_list_get(list)))
737 silc_client_del_client(client, conn, entry->context);
742 if (conn->internal->client_cache)
743 silc_idcache_free(conn->internal->client_cache);
744 if (conn->internal->channel_cache)
745 silc_idcache_free(conn->internal->channel_cache);
746 if (conn->internal->server_cache)
747 silc_idcache_free(conn->internal->server_cache);
749 /* Free thread pool */
750 silc_list_start(conn->internal->thread_pool);
751 while ((thread = silc_list_get(conn->internal->thread_pool)))
752 silc_fsm_free(thread);
754 silc_free(conn->remote_host);
755 silc_buffer_free(conn->internal->local_idp);
756 silc_buffer_free(conn->internal->remote_idp);
757 silc_mutex_free(conn->internal->lock);
758 if (conn->internal->hash)
759 silc_hash_free(conn->internal->hash);
760 if (conn->internal->sha1hash)
761 silc_hash_free(conn->internal->sha1hash);
762 silc_atomic_uninit16(&conn->internal->cmd_ident);
763 silc_free(conn->internal->away_message);
764 if (conn->internal->rekey)
765 silc_ske_free_rekey_material(conn->internal->rekey);
766 if (conn->internal->cop)
767 silc_async_free(conn->internal->cop);
769 silc_free(conn->internal);
770 memset(conn, 'F', sizeof(*conn));
774 /******************************* Client API *********************************/
776 /* Connects to remote server. This is the main routine used to connect
777 to remote SILC server. Performs key exchange also. Returns the
778 connection context to the connection callback. */
781 silc_client_connect_to_server(SilcClient client,
782 SilcClientConnectionParams *params,
783 SilcPublicKey public_key,
784 SilcPrivateKey private_key,
785 char *remote_host, int port,
786 SilcClientConnectCallback callback,
789 SilcClientConnection conn;
791 SILC_LOG_DEBUG(("Connecting to server"));
793 if (!client || !remote_host || !callback)
796 if (client->internal->run_callback) {
797 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
798 "callback has not been called yet."));
802 /* Add new connection */
803 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
804 public_key, private_key, remote_host,
805 port, callback, context);
807 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
811 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
812 "Connecting to port %d of server %s",
815 /* Signal connection machine to start connecting */
816 conn->internal->connect = TRUE;
817 return conn->internal->cop;
820 /* Connects to remote client. Performs key exchange also. Returns the
821 connection context to the connection callback. */
824 silc_client_connect_to_client(SilcClient client,
825 SilcClientConnectionParams *params,
826 SilcPublicKey public_key,
827 SilcPrivateKey private_key,
828 char *remote_host, int port,
829 SilcClientConnectCallback callback,
832 SilcClientConnection conn;
834 SILC_LOG_DEBUG(("Connecting to client"));
836 if (!client || !remote_host || !callback)
839 if (client->internal->run_callback) {
840 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
841 "callback has not been called yet."));
846 params->no_authentication = TRUE;
848 /* Add new connection */
849 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
850 public_key, private_key, remote_host,
851 port, callback, context);
853 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
857 /* Signal connection machine to start connecting */
858 conn->internal->connect = TRUE;
859 return conn->internal->cop;
862 /* Starts key exchange in the remote stream indicated by `stream'. This
863 creates the connection context and returns it in the connection callback. */
866 silc_client_key_exchange(SilcClient client,
867 SilcClientConnectionParams *params,
868 SilcPublicKey public_key,
869 SilcPrivateKey private_key,
871 SilcConnectionType conn_type,
872 SilcClientConnectCallback callback,
875 SilcClientConnection conn;
879 SILC_LOG_DEBUG(("Performing key exchange"));
881 if (!client || !stream || !callback)
884 if (client->internal->run_callback) {
885 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
886 "callback has not been called yet."));
890 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
891 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
892 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
896 /* Add new connection */
897 conn = silc_client_add_connection(client, conn_type, TRUE, params,
898 public_key, private_key,
899 (char *)host, port, callback, context);
901 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
904 conn->internal->user_stream = stream;
906 /* Signal connection to start key exchange */
907 conn->internal->key_exchange = TRUE;
908 return conn->internal->cop;
911 /* Closes remote connection */
913 void silc_client_close_connection(SilcClient client,
914 SilcClientConnection conn)
916 SILC_LOG_DEBUG(("Closing connection %p", conn));
918 /* Signal to close connection */
919 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
920 if (!conn->internal->disconnected) {
921 conn->internal->disconnected = TRUE;
922 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
926 /* Allocates new client object. This has to be done before client may
927 work. After calling this one must call silc_client_init to initialize
928 the client. The `application' is application specific user data pointer
929 and caller must free it. */
931 SilcClient silc_client_alloc(SilcClientOperations *ops,
932 SilcClientParams *params,
934 const char *version_string)
936 SilcClient new_client;
938 new_client = silc_calloc(1, sizeof(*new_client));
941 new_client->application = application;
943 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
944 if (!new_client->internal) {
945 silc_free(new_client);
948 new_client->internal->ops = ops;
949 new_client->internal->params =
950 silc_calloc(1, sizeof(*new_client->internal->params));
952 version_string = silc_version_string;
953 new_client->internal->silc_client_version = strdup(version_string);
956 memcpy(new_client->internal->params, params, sizeof(*params));
958 new_client->internal->params->
959 nickname_format[sizeof(new_client->internal->
960 params->nickname_format) - 1] = 0;
962 silc_atomic_init32(&new_client->internal->conns, 0);
967 /* Frees client object and its internals. */
969 void silc_client_free(SilcClient client)
971 if (client->schedule)
972 silc_schedule_uninit(client->schedule);
975 silc_rng_free(client->rng);
977 if (!client->internal->params->dont_register_crypto_library) {
978 silc_cipher_unregister_all();
979 silc_pkcs_unregister_all();
980 silc_hash_unregister_all();
981 silc_hmac_unregister_all();
984 if (client->internal->packet_engine)
985 silc_packet_engine_stop(client->internal->packet_engine);
986 if (client->internal->ftp_sessions)
987 silc_dlist_uninit(client->internal->ftp_sessions);
988 if (client->internal->lock)
989 silc_mutex_free(client->internal->lock);
990 silc_atomic_uninit32(&client->internal->conns);
991 silc_free(client->username);
992 silc_free(client->hostname);
993 silc_free(client->realname);
994 silc_free(client->internal->params);
995 silc_free(client->internal->silc_client_version);
996 silc_free(client->internal);
1000 /* Initializes the client. This makes all the necessary steps to make
1001 the client ready to be run. One must call silc_client_run to run the
1002 client. Returns FALSE if error occured, TRUE otherwise. */
1004 SilcBool silc_client_init(SilcClient client, const char *username,
1005 const char *hostname, const char *realname,
1006 SilcClientRunning running, void *context)
1008 SILC_LOG_DEBUG(("Initializing client"));
1013 if (!username || !hostname) {
1014 SILC_LOG_ERROR(("Username and hostname must be given to "
1015 "silc_client_init"));
1019 realname = username;
1021 /* Validate essential strings */
1022 if (!silc_identifier_verify(username, strlen(username),
1023 SILC_STRING_UTF8, 128)) {
1024 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1028 if (!silc_identifier_verify(hostname, strlen(hostname),
1029 SILC_STRING_UTF8, 256)) {
1030 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1034 if (!silc_utf8_valid(realname, strlen(realname))) {
1035 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1040 /* Take the name strings */
1041 client->username = strdup(username);
1042 client->hostname = strdup(hostname);
1043 client->realname = strdup(realname);
1044 if (!username || !hostname || !realname)
1047 client->internal->ftp_sessions = silc_dlist_init();
1048 if (!client->internal->ftp_sessions)
1051 if (!client->internal->params->dont_register_crypto_library) {
1052 /* Initialize the crypto library. If application has done this already
1053 this has no effect. Also, we will not be overriding something
1054 application might have registered earlier. */
1055 silc_cipher_register_default();
1056 silc_pkcs_register_default();
1057 silc_hash_register_default();
1058 silc_hmac_register_default();
1061 /* Initialize random number generator */
1062 client->rng = silc_rng_alloc();
1065 silc_rng_init(client->rng);
1066 silc_rng_global_init(client->rng);
1068 /* Initialize the scheduler */
1069 client->schedule = silc_schedule_init(0, client);
1070 if (!client->schedule)
1073 /* Allocate client lock */
1074 silc_mutex_alloc(&client->internal->lock);
1076 /* Register commands */
1077 silc_client_commands_register(client);
1079 /* Start packet engine */
1080 client->internal->packet_engine =
1081 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1083 if (!client->internal->packet_engine)
1086 /* Initialize and start the client FSM */
1087 client->internal->running = running;
1088 client->internal->running_context = context;
1089 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1090 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1091 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1093 /* Signal the application when we are running */
1094 client->internal->run_callback = TRUE;
1095 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1100 /* Starts the SILC client FSM machine and blocks here. When this returns
1101 the client has ended. */
1103 void silc_client_run(SilcClient client)
1105 SILC_LOG_DEBUG(("Starting SILC client"));
1107 /* Run the scheduler */
1108 silc_schedule(client->schedule);
1111 /* Call scheduler one iteration and return. */
1113 void silc_client_run_one(SilcClient client)
1115 if (silc_fsm_is_started(&client->internal->fsm))
1116 silc_schedule_one(client->schedule, 0);
1119 /* Stops the client. This is called to stop the client and thus to stop
1122 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1125 SILC_LOG_DEBUG(("Stopping client"));
1127 if (!silc_fsm_is_started(&client->internal->fsm)) {
1128 SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1129 "started (silc_client_init not called)"));
1133 client->internal->running = (SilcClientRunning)stopped;
1134 client->internal->running_context = context;
1136 /* Signal to stop */
1137 client->internal->stop = TRUE;
1138 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);