5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2008 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.
21 #include "silcclient.h"
22 #include "client_internal.h"
24 /************************ Static utility functions **************************/
26 /* Connection machine FSM destructor. This will finish the thread where
27 the machine was running and deletes the connection context. */
29 static void silc_client_connection_destructor(SilcFSM fsm,
31 void *destructor_context)
33 SilcClientConnection conn = fsm_context;
34 SilcFSMThread thread = destructor_context;
36 SILC_LOG_DEBUG(("Connection %p finished", conn));
38 /* Delete connection */
39 silc_client_del_connection(conn->client, conn);
41 /* Finish the thread were this machine was running. Its destructor is the
42 silc_client_connection_finished. */
43 silc_fsm_finish(thread);
46 /* Connection thread FSM destructor. This was the thread where the connection
47 machine was running (may be real thread). From here we notify client
48 that the connection thread has finished. */
50 static void silc_client_connection_finished(SilcFSMThread fsm,
52 void *destructor_context)
54 SilcClient client = silc_fsm_get_state_context(fsm);
56 /* Signal client that we have finished */
57 silc_atomic_sub_int32(&client->internal->conns, 1);
58 client->internal->connection_closed = TRUE;
59 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
64 /* Packet FSM thread destructor */
66 static void silc_client_packet_destructor(SilcFSMThread thread,
68 void *destructor_context)
70 SilcClientConnection conn = thread_context;
72 /* Add thread back to thread pool */
73 silc_list_add(conn->internal->thread_pool, thread);
74 if (silc_list_count(conn->internal->thread_pool) == 1)
75 silc_list_start(conn->internal->thread_pool);
78 /* Packet engine callback to receive a packet */
80 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
81 SilcPacketStream stream,
83 void *callback_context,
86 SilcClientConnection conn = stream_context;
89 /* Packets we do not handle */
90 switch (packet->type) {
91 case SILC_PACKET_HEARTBEAT:
92 case SILC_PACKET_SUCCESS:
93 case SILC_PACKET_FAILURE:
94 case SILC_PACKET_REJECT:
95 case SILC_PACKET_KEY_EXCHANGE:
96 case SILC_PACKET_KEY_EXCHANGE_1:
97 case SILC_PACKET_KEY_EXCHANGE_2:
98 case SILC_PACKET_REKEY_DONE:
99 case SILC_PACKET_CONNECTION_AUTH:
104 /* Get packet processing thread */
105 thread = silc_list_get(conn->internal->thread_pool);
107 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
108 silc_client_packet_destructor, NULL, FALSE);
112 silc_list_del(conn->internal->thread_pool, thread);
113 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
114 silc_client_packet_destructor, NULL, FALSE);
117 /* Process packet in thread */
118 silc_fsm_set_state_context(thread, packet);
119 silc_fsm_start_sync(thread, silc_client_connection_st_packet);
124 /* Packet engine callback to indicate end of stream */
126 static void silc_client_packet_eos(SilcPacketEngine engine,
127 SilcPacketStream stream,
128 void *callback_context,
129 void *stream_context)
131 SilcClientConnection conn = stream_context;
133 SILC_LOG_DEBUG(("Remote disconnected connection"));
135 /* Signal to close connection */
136 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
137 if (!conn->internal->disconnected) {
138 conn->internal->disconnected = TRUE;
139 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
143 /* Packet engine callback to indicate error */
145 static void silc_client_packet_error(SilcPacketEngine engine,
146 SilcPacketStream stream,
147 SilcPacketError error,
148 void *callback_context,
149 void *stream_context)
151 SilcClient client = callback_context;
152 SilcClientConnection conn = stream_context;
154 /* Read and write errors are silent */
155 if (error == SILC_PACKET_ERR_READ || error == SILC_PACKET_ERR_WRITE)
158 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
159 (char *)silc_packet_error_string(error));
162 /* Packet stream callbacks */
163 static SilcPacketCallbacks silc_client_stream_cbs =
165 silc_client_packet_receive,
166 silc_client_packet_eos,
167 silc_client_packet_error
172 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
173 void *destructor_context)
178 /* Connect abort operation */
180 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
182 SilcClientConnection conn = context;
184 SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
186 /* Connection callback will not be called after user aborted connecting */
187 conn->callback = NULL;
188 conn->internal->cop = NULL;
190 /* Signal to close connection */
191 if (!conn->internal->disconnected) {
192 conn->internal->disconnected = TRUE;
194 /* If user aborts before connection machine is even up yet, then don't
195 send signal yet. It will process this event when it comes up. */
196 if (silc_fsm_is_started(&conn->internal->fsm))
197 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
201 /************************** Connection's machine ****************************/
203 /* Start the connection's state machine. If threads are in use the machine
204 is always executed in a real thread. */
206 SILC_FSM_STATE(silc_client_connection_st_start)
208 SilcClientConnection conn = fsm_context;
211 /* Take scheduler for connection */
212 conn->internal->schedule = silc_fsm_get_schedule(fsm);
214 /*** Run connection machine */
215 connfsm = &conn->internal->fsm;
216 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
217 fsm, conn->internal->schedule);
218 silc_fsm_event_init(&conn->internal->wait_event, connfsm);
219 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
221 /* Schedule any events possibly set in initialization */
222 if (conn->internal->disconnected)
223 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
224 if (conn->internal->connect)
225 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
226 if (conn->internal->key_exchange)
227 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
229 /* Wait until this thread is terminated from the machine destructor */
230 return SILC_FSM_WAIT;
233 /* Connection machine main state. This handles various connection related
234 events, but not packet processing. It's done in dedicated packet
235 processing FSM thread. */
237 SILC_FSM_STATE(silc_client_connection_st_run)
239 SilcClientConnection conn = fsm_context;
240 SilcFSMThread thread;
242 /* Wait for events */
243 SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
246 thread = &conn->internal->event_thread;
248 if (conn->internal->disconnected) {
249 /** Event: disconnected */
250 SILC_LOG_DEBUG(("Event: disconnected"));
251 silc_fsm_next(fsm, silc_client_connection_st_close);
252 return SILC_FSM_YIELD;
255 if (conn->internal->connect) {
256 SILC_LOG_DEBUG(("Event: connect"));
257 conn->internal->connect = FALSE;
258 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
260 /*** Event: connect */
261 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
263 silc_fsm_start_sync(thread, silc_client_st_connect);
264 return SILC_FSM_CONTINUE;
267 if (conn->internal->key_exchange) {
268 SILC_LOG_DEBUG(("Event: key exchange"));
269 conn->internal->key_exchange = FALSE;
270 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
272 /*** Event: key exchange */
273 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
275 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
276 return SILC_FSM_CONTINUE;
279 if (conn->internal->rekeying) {
280 SILC_LOG_DEBUG(("Event: rekey"));
281 conn->internal->rekeying = FALSE;
282 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
285 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
287 silc_fsm_start_sync(thread, silc_client_st_rekey);
288 return SILC_FSM_CONTINUE;
293 return SILC_FSM_CONTINUE;
296 /* Packet processor thread. Each incoming packet is processed in FSM
297 thread in this state. The thread is run in the connection machine. */
299 SILC_FSM_STATE(silc_client_connection_st_packet)
301 SilcClientConnection conn = fsm_context;
302 SilcPacket packet = state_context;
304 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
306 switch (packet->type) {
308 case SILC_PACKET_PRIVATE_MESSAGE:
309 /** Private message */
310 silc_fsm_next(fsm, silc_client_private_message);
313 case SILC_PACKET_CHANNEL_MESSAGE:
314 /** Channel message */
315 silc_fsm_next(fsm, silc_client_channel_message);
318 case SILC_PACKET_FTP:
319 /* File transfer packet */
320 silc_fsm_next(fsm, silc_client_ftp);
323 case SILC_PACKET_CHANNEL_KEY:
325 silc_fsm_next(fsm, silc_client_channel_key);
328 case SILC_PACKET_COMMAND_REPLY:
330 silc_fsm_next(fsm, silc_client_command_reply);
333 case SILC_PACKET_NOTIFY:
335 silc_fsm_next(fsm, silc_client_notify);
338 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
339 /* Private message key indicator */
340 silc_fsm_next(fsm, silc_client_private_message_key);
343 case SILC_PACKET_DISCONNECT:
345 silc_fsm_next(fsm, silc_client_disconnect);
348 case SILC_PACKET_ERROR:
349 /* Error by server */
350 silc_fsm_next(fsm, silc_client_error);
353 case SILC_PACKET_KEY_AGREEMENT:
355 silc_fsm_next(fsm, silc_client_key_agreement);
358 case SILC_PACKET_COMMAND:
359 /** Command packet */
360 silc_fsm_next(fsm, silc_client_command);
363 case SILC_PACKET_NEW_ID:
365 silc_fsm_next(fsm, silc_client_new_id);
368 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
369 /** Connection auth resolve reply */
370 silc_fsm_next(fsm, silc_client_connect_auth_request);
373 case SILC_PACKET_REKEY:
374 /* Signal to start rekey */
375 conn->internal->rekey_responder = TRUE;
376 conn->internal->rekeying = TRUE;
377 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
379 silc_packet_free(packet);
380 return SILC_FSM_FINISH;
384 silc_packet_free(packet);
385 return SILC_FSM_FINISH;
389 return SILC_FSM_CONTINUE;
392 /* Disconnection event to close remote connection. We close the connection
393 and finish the connection machine in this state. The connection context
394 is deleted in the machine destructor. The connection callback is called
395 in this state if it is set. */
397 SILC_FSM_STATE(silc_client_connection_st_close)
399 SilcClientConnection conn = fsm_context;
400 SilcClientCommandContext cmd;
402 /* Finish running command threads. This will also finish waiting packet
403 thread, as they are always waiting for some command. If any thread is
404 waiting something else than command, they must be finished explicitly. */
405 if (silc_list_count(conn->internal->pending_commands)) {
406 SILC_LOG_DEBUG(("Finish pending commands"));
407 silc_list_start(conn->internal->pending_commands);
408 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
409 if (silc_fsm_is_started(&cmd->thread)) {
410 cmd->verbose = FALSE;
411 silc_fsm_continue_sync(&cmd->thread);
415 /* Give threads time to finish */
416 return SILC_FSM_YIELD;
419 /* Abort ongoing event */
420 if (conn->internal->op) {
421 SILC_LOG_DEBUG(("Abort event"));
422 silc_async_abort(conn->internal->op, NULL, NULL);
423 conn->internal->op = NULL;
426 /* If event thread is running, finish it. */
427 if (silc_fsm_is_started(&conn->internal->event_thread)) {
428 SILC_LOG_DEBUG(("Finish event thread"));
429 silc_fsm_continue_sync(&conn->internal->event_thread);
430 return SILC_FSM_YIELD;
433 /* Call the connection callback */
435 conn->callback(conn->client, conn, conn->internal->status,
436 conn->internal->error, conn->internal->disconnect_message,
437 conn->callback_context);
438 silc_free(conn->internal->disconnect_message);
440 SILC_LOG_DEBUG(("Closing remote connection"));
442 /* Close connection. */
444 silc_packet_stream_destroy(conn->stream);
446 SILC_LOG_DEBUG(("Finishing connection machine"));
447 return SILC_FSM_FINISH;
450 /* Received error packet from server. Send it to application. */
452 SILC_FSM_STATE(silc_client_error)
454 SilcClientConnection conn = fsm_context;
455 SilcClient client = conn->client;
456 SilcPacket packet = state_context;
459 msg = silc_memdup(silc_buffer_data(&packet->buffer),
460 silc_buffer_len(&packet->buffer));
462 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
465 silc_packet_free(packet);
467 return SILC_FSM_FINISH;
470 /* Received disconnect packet from server. We close the connection and
471 send the disconnect message to application. */
473 SILC_FSM_STATE(silc_client_disconnect)
475 SilcClientConnection conn = fsm_context;
476 SilcPacket packet = state_context;
478 char *message = NULL;
480 SILC_LOG_DEBUG(("Server disconnected"));
482 if (silc_buffer_len(&packet->buffer) < 1) {
483 silc_packet_free(packet);
484 return SILC_FSM_FINISH;
487 status = (SilcStatus)packet->buffer.data[0];
489 silc_buffer_pull(&packet->buffer, 1);
490 if (silc_buffer_len(&packet->buffer) > 1 &&
491 silc_utf8_valid(silc_buffer_data(&packet->buffer),
492 silc_buffer_len(&packet->buffer)))
493 message = silc_memdup(silc_buffer_data(&packet->buffer),
494 silc_buffer_len(&packet->buffer));
496 /* Call connection callback */
497 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
498 conn->internal->error = status;
499 conn->internal->disconnect_message = message;
501 /* Signal to close connection */
502 if (!conn->internal->disconnected) {
503 conn->internal->disconnected = TRUE;
504 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
507 silc_packet_free(packet);
509 return SILC_FSM_FINISH;
512 /*************************** Main client machine ****************************/
514 /* The client's main state where we wait for various events */
516 SILC_FSM_STATE(silc_client_st_run)
518 SilcClient client = fsm_context;
520 /* Wait for events */
521 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
525 if (client->internal->run_callback) {
526 /* Call running callbcak back to application */
527 client->internal->run_callback = FALSE;
528 if (client->internal->running) {
529 SILC_LOG_DEBUG(("We are up, call running callback"));
530 client->internal->running(client, client->internal->running_context);
532 return SILC_FSM_CONTINUE;
535 if (client->internal->connection_closed) {
536 /* A connection finished */
537 SILC_LOG_DEBUG(("Event: connection closed"));
538 client->internal->connection_closed = FALSE;
539 if (silc_atomic_get_int32(&client->internal->conns) == 0 &&
540 client->internal->stop)
541 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
542 return SILC_FSM_CONTINUE;
545 if (client->internal->stop) {
546 /* Stop client libarry. If we have running connections, wait until
547 they finish first. */
548 if (silc_atomic_get_int32(&client->internal->conns) == 0) {
549 SILC_LOG_DEBUG(("Event: stop"));
550 silc_fsm_next(fsm, silc_client_st_stop);
552 return SILC_FSM_CONTINUE;
557 return SILC_FSM_CONTINUE;
560 /* Stop event. Stops the client library. */
562 SILC_FSM_STATE(silc_client_st_stop)
564 SilcClient client = fsm_context;
566 SILC_LOG_DEBUG(("Client stopped"));
569 silc_schedule_stop(client->schedule);
570 silc_client_commands_unregister(client);
572 /* Call stopped callback to application */
573 if (client->internal->running)
574 client->internal->running(client, client->internal->running_context);
576 return SILC_FSM_FINISH;
579 /******************************* Private API ********************************/
581 /* Adds new connection. Creates the connection context and returns it. */
584 silc_client_add_connection(SilcClient client,
585 SilcConnectionType conn_type,
587 SilcClientConnectionParams *params,
588 SilcPublicKey public_key,
589 SilcPrivateKey private_key,
590 char *remote_host, int port,
591 SilcClientConnectCallback callback,
594 SilcClientConnection conn;
595 SilcFSMThread thread;
600 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
602 conn = silc_calloc(1, sizeof(*conn));
606 conn->client = client;
607 conn->public_key = public_key;
608 conn->private_key = private_key;
609 conn->remote_host = strdup(remote_host);
610 conn->remote_port = port ? port : 706;
611 conn->type = conn_type;
612 conn->callback = callback;
613 conn->callback_context = context;
615 conn->internal = silc_calloc(1, sizeof(*conn->internal));
616 if (!conn->internal) {
620 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
621 silc_mutex_alloc(&conn->internal->lock);
622 silc_atomic_init16(&conn->internal->cmd_ident, 0);
624 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
626 silc_free(conn->internal);
632 conn->internal->params = *params;
633 if (!conn->internal->params.rekey_secs)
634 conn->internal->params.rekey_secs = 3600;
635 #ifndef SILC_DIST_INPLACE
636 if (conn->internal->params.rekey_secs < 300)
637 conn->internal->params.rekey_secs = 300;
638 #endif /* SILC_DIST_INPLACE */
640 conn->internal->verbose = TRUE;
641 silc_list_init(conn->internal->pending_commands,
642 struct SilcClientCommandContextStruct, next);
643 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
645 /* Allocate client, channel and serve caches */
646 if (conn_type != SILC_CONN_CLIENT) {
647 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
649 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
651 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
653 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
654 !conn->internal->server_cache) {
655 silc_client_del_connection(client, conn);
661 /* Initialize our async operation so that application may abort us
662 while we're connecting. */
663 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
665 if (!conn->internal->cop) {
666 silc_client_del_connection(client, conn);
671 /* Run the connection state machine. If threads are in use the connection
672 machine is always run in a real thread. */
673 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
674 silc_client_connection_finished, NULL,
675 client->internal->params->threads);
677 silc_client_del_connection(client, conn);
680 silc_fsm_set_state_context(thread, client);
681 silc_fsm_start(thread, silc_client_connection_st_start);
683 SILC_LOG_DEBUG(("New connection %p", conn));
684 silc_atomic_add_int32(&client->internal->conns, 1);
689 /* Deletes connection. This is always called from the connection machine
690 destructor. Do not call this directly other places. */
692 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
695 SilcIDCacheEntry entry;
696 SilcFSMThread thread;
698 SILC_LOG_DEBUG(("Freeing connection %p", conn));
700 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
702 /* Free all cache entries */
703 if (conn->internal->server_cache) {
704 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
705 silc_list_start(list);
706 while ((entry = silc_list_get(list)))
707 silc_client_del_server(client, conn, entry->context);
710 if (conn->internal->channel_cache) {
711 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
712 silc_list_start(list);
713 while ((entry = silc_list_get(list))) {
714 silc_client_empty_channel(client, conn, entry->context);
715 silc_client_del_channel(client, conn, entry->context);
719 if (conn->internal->client_cache) {
720 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
721 silc_list_start(list);
722 while ((entry = silc_list_get(list)))
723 silc_client_del_client(client, conn, entry->context);
728 if (conn->internal->client_cache)
729 silc_idcache_free(conn->internal->client_cache);
730 if (conn->internal->channel_cache)
731 silc_idcache_free(conn->internal->channel_cache);
732 if (conn->internal->server_cache)
733 silc_idcache_free(conn->internal->server_cache);
735 /* Free thread pool */
736 silc_list_start(conn->internal->thread_pool);
737 while ((thread = silc_list_get(conn->internal->thread_pool)))
738 silc_fsm_free(thread);
740 silc_free(conn->remote_host);
741 silc_buffer_free(conn->internal->local_idp);
742 silc_buffer_free(conn->internal->remote_idp);
743 silc_mutex_free(conn->internal->lock);
744 if (conn->internal->hash)
745 silc_hash_free(conn->internal->hash);
746 if (conn->internal->sha1hash)
747 silc_hash_free(conn->internal->sha1hash);
748 silc_atomic_uninit16(&conn->internal->cmd_ident);
749 silc_free(conn->internal->away_message);
750 if (conn->internal->rekey)
751 silc_ske_free_rekey_material(conn->internal->rekey);
752 if (conn->internal->cop)
753 silc_async_free(conn->internal->cop);
755 silc_free(conn->internal);
756 memset(conn, 'F', sizeof(*conn));
760 /******************************* Client API *********************************/
762 /* Connects to remote server. This is the main routine used to connect
763 to remote SILC server. Performs key exchange also. Returns the
764 connection context to the connection callback. */
767 silc_client_connect_to_server(SilcClient client,
768 SilcClientConnectionParams *params,
769 SilcPublicKey public_key,
770 SilcPrivateKey private_key,
771 char *remote_host, int port,
772 SilcClientConnectCallback callback,
775 SilcClientConnection conn;
777 SILC_LOG_DEBUG(("Connecting to server"));
779 if (!client || !remote_host)
782 if (client->internal->run_callback) {
783 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
784 "callback has not been called yet."));
788 /* Add new connection */
789 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
790 public_key, private_key, remote_host,
791 port, callback, context);
793 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
797 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
798 "Connecting to port %d of server %s",
801 /* Signal connection machine to start connecting */
802 conn->internal->connect = TRUE;
803 return conn->internal->cop;
806 /* Connects to remote client. Performs key exchange also. Returns the
807 connection context to the connection callback. */
810 silc_client_connect_to_client(SilcClient client,
811 SilcClientConnectionParams *params,
812 SilcPublicKey public_key,
813 SilcPrivateKey private_key,
814 char *remote_host, int port,
815 SilcClientConnectCallback callback,
818 SilcClientConnection conn;
820 SILC_LOG_DEBUG(("Connecting to client"));
822 if (!client || !remote_host)
825 if (client->internal->run_callback) {
826 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
827 "callback has not been called yet."));
832 params->no_authentication = TRUE;
834 /* Add new connection */
835 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
836 public_key, private_key, remote_host,
837 port, callback, context);
839 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
843 /* Signal connection machine to start connecting */
844 conn->internal->connect = TRUE;
845 return conn->internal->cop;
848 /* Starts key exchange in the remote stream indicated by `stream'. This
849 creates the connection context and returns it in the connection callback. */
852 silc_client_key_exchange(SilcClient client,
853 SilcClientConnectionParams *params,
854 SilcPublicKey public_key,
855 SilcPrivateKey private_key,
857 SilcConnectionType conn_type,
858 SilcClientConnectCallback callback,
861 SilcClientConnection conn;
865 SILC_LOG_DEBUG(("Performing key exchange"));
867 if (!client || !stream)
870 if (client->internal->run_callback) {
871 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
872 "callback has not been called yet."));
876 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
877 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
878 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
882 /* Add new connection */
883 conn = silc_client_add_connection(client, conn_type, TRUE, params,
884 public_key, private_key,
885 (char *)host, port, callback, context);
887 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
890 conn->internal->user_stream = stream;
892 /* Signal connection to start key exchange */
893 conn->internal->key_exchange = TRUE;
894 return conn->internal->cop;
897 /* Closes remote connection */
899 void silc_client_close_connection(SilcClient client,
900 SilcClientConnection conn)
902 SILC_LOG_DEBUG(("Closing connection %p", conn));
904 /* Signal to close connection */
905 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
906 if (!conn->internal->disconnected) {
907 conn->internal->disconnected = TRUE;
908 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
912 /* Allocates new client object. This has to be done before client may
913 work. After calling this one must call silc_client_init to initialize
914 the client. The `application' is application specific user data pointer
915 and caller must free it. */
917 SilcClient silc_client_alloc(SilcClientOperations *ops,
918 SilcClientParams *params,
920 const char *version_string)
922 SilcClient new_client;
924 new_client = silc_calloc(1, sizeof(*new_client));
927 new_client->application = application;
929 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
930 if (!new_client->internal) {
931 silc_free(new_client);
934 new_client->internal->ops = ops;
935 new_client->internal->params =
936 silc_calloc(1, sizeof(*new_client->internal->params));
938 version_string = silc_version_string;
939 new_client->internal->silc_client_version = strdup(version_string);
942 memcpy(new_client->internal->params, params, sizeof(*params));
944 new_client->internal->params->
945 nickname_format[sizeof(new_client->internal->
946 params->nickname_format) - 1] = 0;
948 silc_atomic_init32(&new_client->internal->conns, 0);
953 /* Frees client object and its internals. */
955 void silc_client_free(SilcClient client)
957 if (client->schedule)
958 silc_schedule_uninit(client->schedule);
961 silc_rng_free(client->rng);
963 if (!client->internal->params->dont_register_crypto_library)
964 silc_crypto_uninit();
966 if (client->internal->packet_engine)
967 silc_packet_engine_stop(client->internal->packet_engine);
968 if (client->internal->ftp_sessions)
969 silc_dlist_uninit(client->internal->ftp_sessions);
970 if (client->internal->lock)
971 silc_mutex_free(client->internal->lock);
972 silc_atomic_uninit32(&client->internal->conns);
973 silc_free(client->username);
974 silc_free(client->hostname);
975 silc_free(client->realname);
976 silc_free(client->internal->params);
977 silc_free(client->internal->silc_client_version);
978 silc_free(client->internal);
982 /* Initializes the client. This makes all the necessary steps to make
983 the client ready to be run. One must call silc_client_run to run the
984 client. Returns FALSE if error occured, TRUE otherwise. */
986 SilcBool silc_client_init(SilcClient client, const char *username,
987 const char *hostname, const char *realname,
988 SilcClientRunning running, void *context)
990 SILC_LOG_DEBUG(("Initializing client"));
995 if (!username || !hostname) {
996 SILC_LOG_ERROR(("Username and hostname must be given to "
997 "silc_client_init"));
1001 realname = username;
1003 /* Validate essential strings */
1004 if (!silc_identifier_verify(username, strlen(username),
1005 SILC_STRING_UTF8, 128)) {
1006 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1010 if (!silc_identifier_verify(hostname, strlen(hostname),
1011 SILC_STRING_UTF8, 256)) {
1012 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1016 if (!silc_utf8_valid(realname, strlen(realname))) {
1017 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1022 /* Take the name strings */
1023 client->username = strdup(username);
1024 client->hostname = strdup(hostname);
1025 client->realname = strdup(realname);
1026 if (!username || !hostname || !realname)
1029 client->internal->ftp_sessions = silc_dlist_init();
1030 if (!client->internal->ftp_sessions)
1033 if (!client->internal->params->dont_register_crypto_library)
1034 /* Initialize the crypto library. If application has done this already
1035 this has no effect. */
1036 silc_crypto_init(NULL);
1038 /* Initialize random number generator */
1039 client->rng = silc_rng_alloc();
1042 silc_rng_init(client->rng);
1043 silc_rng_global_init(client->rng);
1045 /* Initialize the scheduler */
1046 client->schedule = silc_schedule_init(0, client, NULL, NULL);
1047 if (!client->schedule)
1050 /* Allocate client lock */
1051 silc_mutex_alloc(&client->internal->lock);
1053 /* Register commands */
1054 silc_client_commands_register(client);
1056 /* Start packet engine */
1057 client->internal->packet_engine =
1058 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1060 if (!client->internal->packet_engine)
1063 /* Initialize and start the client FSM */
1064 client->internal->running = running;
1065 client->internal->running_context = context;
1066 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1067 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1068 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1070 /* Signal the application when we are running */
1071 client->internal->run_callback = TRUE;
1072 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1077 /* Starts the SILC client FSM machine and blocks here. When this returns
1078 the client has ended. */
1080 void silc_client_run(SilcClient client)
1082 SILC_LOG_DEBUG(("Starting SILC client"));
1084 /* Run the scheduler */
1085 silc_schedule(client->schedule);
1088 /* Call scheduler one iteration and return. */
1090 void silc_client_run_one(SilcClient client)
1092 if (silc_fsm_is_started(&client->internal->fsm))
1093 silc_schedule_one(client->schedule, 0);
1096 /* Stops the client. This is called to stop the client and thus to stop
1099 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1102 SILC_LOG_DEBUG(("Stopping client"));
1104 if (!silc_fsm_is_started(&client->internal->fsm)) {
1105 SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1106 "started (silc_client_init not called)"));
1110 client->internal->running = (SilcClientRunning)stopped;
1111 client->internal->running_context = context;
1113 /* Signal to stop */
1114 client->internal->stop = TRUE;
1115 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);