5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
28 /************************ Static utility functions **************************/
30 /* Connection machine FSM destructor. This will finish the thread where
31 the machine was running and deletes the connection context. */
33 static void silc_client_connection_destructor(SilcFSM fsm,
35 void *destructor_context)
37 SilcClientConnection conn = fsm_context;
38 SilcFSMThread thread = destructor_context;
40 SILC_LOG_DEBUG(("Connection %p finished", conn));
42 /* Delete connection */
43 silc_client_del_connection(conn->client, conn);
45 /* Finish the thread were this machine was running */
46 silc_fsm_finish(thread);
49 /* Connection thread FSM destructor. This was the thread where the connection
50 machine was running (may be real thread). From here we notify client
51 that the connection thread has finished. */
53 static void silc_client_connection_finished(SilcFSMThread fsm,
55 void *destructor_context)
57 SilcClient client = silc_fsm_get_state_context(fsm);
59 /* Signal client that we have finished */
60 silc_atomic_sub_int16(&client->internal->conns, 1);
61 client->internal->connection_closed = TRUE;
62 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
67 /* Packet FSM thread destructor */
69 static void silc_client_packet_destructor(SilcFSMThread thread,
71 void *destructor_context)
73 SilcClientConnection conn = thread_context;
75 /* Add thread back to thread pool */
76 silc_list_add(conn->internal->thread_pool, thread);
77 if (silc_list_count(conn->internal->thread_pool) == 1)
78 silc_list_start(conn->internal->thread_pool);
81 /* Packet engine callback to receive a packet */
83 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
84 SilcPacketStream stream,
86 void *callback_context,
89 SilcClientConnection conn = stream_context;
92 /* Packets we do not handle */
93 switch (packet->type) {
94 case SILC_PACKET_HEARTBEAT:
95 case SILC_PACKET_SUCCESS:
96 case SILC_PACKET_FAILURE:
97 case SILC_PACKET_REJECT:
98 case SILC_PACKET_KEY_EXCHANGE:
99 case SILC_PACKET_KEY_EXCHANGE_1:
100 case SILC_PACKET_KEY_EXCHANGE_2:
101 case SILC_PACKET_REKEY_DONE:
102 case SILC_PACKET_CONNECTION_AUTH:
107 /* Get packet processing thread */
108 thread = silc_list_get(conn->internal->thread_pool);
110 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
111 silc_client_packet_destructor, NULL, FALSE);
115 silc_list_del(conn->internal->thread_pool, thread);
116 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
117 silc_client_packet_destructor, NULL, FALSE);
120 /* Process packet in thread */
121 silc_fsm_set_state_context(thread, packet);
122 silc_fsm_start_sync(thread, silc_client_connection_st_packet);
127 /* Packet engine callback to indicate end of stream */
129 static void silc_client_packet_eos(SilcPacketEngine engine,
130 SilcPacketStream stream,
131 void *callback_context,
132 void *stream_context)
134 SilcClientConnection conn = stream_context;
136 SILC_LOG_DEBUG(("Remote disconnected connection"));
138 /* Signal to close connection */
139 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
140 if (!conn->internal->disconnected) {
141 conn->internal->disconnected = TRUE;
142 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
146 /* Packet engine callback to indicate error */
148 static void silc_client_packet_error(SilcPacketEngine engine,
149 SilcPacketStream stream,
150 SilcPacketError error,
151 void *callback_context,
152 void *stream_context)
157 /* Packet stream callbacks */
158 static SilcPacketCallbacks silc_client_stream_cbs =
160 silc_client_packet_receive,
161 silc_client_packet_eos,
162 silc_client_packet_error
167 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
168 void *destructor_context)
173 /* Connect abort operation */
175 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
177 SilcClientConnection conn = context;
179 SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
181 /* Connection callback will not be called after user aborted connecting */
182 conn->callback = NULL;
184 /* Signal to close connection */
185 if (!conn->internal->disconnected) {
186 conn->internal->disconnected = TRUE;
187 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
191 /************************** Connection's machine ****************************/
193 /* Start the connection's state machine. If threads are in use the machine
194 is always executed in a real thread. */
196 SILC_FSM_STATE(silc_client_connection_st_start)
198 SilcClientConnection conn = fsm_context;
201 /* Take scheduler for connection */
202 conn->internal->schedule = silc_fsm_get_schedule(fsm);
204 /*** Run connection machine */
205 connfsm = &conn->internal->fsm;
206 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
207 fsm, conn->internal->schedule);
208 silc_fsm_event_init(&conn->internal->wait_event, connfsm);
209 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
211 /* Schedule any events set in initialization */
212 if (conn->internal->connect)
213 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
214 if (conn->internal->key_exchange)
215 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
217 /* Wait until this thread is terminated from the machine destructor */
218 return SILC_FSM_WAIT;
221 /* Connection machine main state. This handles various connection related
222 events, but not packet processing. It's done in dedicated packet
223 processing FSM thread. */
225 SILC_FSM_STATE(silc_client_connection_st_run)
227 SilcClientConnection conn = fsm_context;
228 SilcFSMThread thread;
230 /* Wait for events */
231 SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
234 thread = &conn->internal->event_thread;
236 if (conn->internal->connect) {
237 SILC_LOG_DEBUG(("Event: connect"));
238 conn->internal->connect = FALSE;
240 /*** Event: connect */
241 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
243 silc_fsm_start_sync(thread, silc_client_st_connect);
244 return SILC_FSM_CONTINUE;
247 if (conn->internal->key_exchange) {
248 SILC_LOG_DEBUG(("Event: key exchange"));
249 conn->internal->key_exchange = FALSE;
251 /*** Event: key exchange */
252 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
254 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
255 return SILC_FSM_CONTINUE;
258 if (conn->internal->rekeying) {
259 SILC_LOG_DEBUG(("Event: rekey"));
260 conn->internal->rekeying = FALSE;
263 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
265 silc_fsm_start_sync(thread, silc_client_st_rekey);
266 return SILC_FSM_CONTINUE;
269 if (conn->internal->disconnected) {
270 /** Event: disconnected */
271 SILC_LOG_DEBUG(("Event: disconnected"));
272 silc_fsm_next(fsm, silc_client_connection_st_close);
273 return SILC_FSM_YIELD;
278 return SILC_FSM_CONTINUE;
281 /* Packet processor thread. Each incoming packet is processed in FSM
282 thread in this state. The thread is run in the connection machine. */
284 SILC_FSM_STATE(silc_client_connection_st_packet)
286 SilcClientConnection conn = fsm_context;
287 SilcPacket packet = state_context;
289 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
291 switch (packet->type) {
293 case SILC_PACKET_PRIVATE_MESSAGE:
294 /** Private message */
295 silc_fsm_next(fsm, silc_client_private_message);
298 case SILC_PACKET_CHANNEL_MESSAGE:
299 /** Channel message */
300 silc_fsm_next(fsm, silc_client_channel_message);
303 case SILC_PACKET_FTP:
304 /* File transfer packet */
305 // silc_fsm_next(fsm, silc_client_ftp);
308 case SILC_PACKET_CHANNEL_KEY:
310 silc_fsm_next(fsm, silc_client_channel_key);
313 case SILC_PACKET_COMMAND_REPLY:
315 silc_fsm_next(fsm, silc_client_command_reply);
318 case SILC_PACKET_NOTIFY:
320 silc_fsm_next(fsm, silc_client_notify);
323 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
324 /* Private message key indicator */
325 silc_fsm_next(fsm, silc_client_private_message_key);
328 case SILC_PACKET_DISCONNECT:
330 silc_fsm_next(fsm, silc_client_disconnect);
333 case SILC_PACKET_ERROR:
334 /* Error by server */
335 silc_fsm_next(fsm, silc_client_error);
338 case SILC_PACKET_KEY_AGREEMENT:
340 silc_fsm_next(fsm, silc_client_key_agreement);
343 case SILC_PACKET_COMMAND:
344 /** Command packet */
345 silc_fsm_next(fsm, silc_client_command);
348 case SILC_PACKET_NEW_ID:
350 silc_fsm_next(fsm, silc_client_new_id);
353 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
354 /** Connection auth resolve reply */
355 silc_fsm_next(fsm, silc_client_connect_auth_request);
358 case SILC_PACKET_REKEY:
359 /* Signal to start rekey */
360 conn->internal->rekey_responder = TRUE;
361 conn->internal->rekeying = TRUE;
362 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
364 silc_packet_free(packet);
365 return SILC_FSM_FINISH;
369 silc_packet_free(packet);
370 return SILC_FSM_FINISH;
374 return SILC_FSM_CONTINUE;
377 /* Disconnection event to close remote connection. We close the connection
378 and finish the connection machine in this state. The connection context
379 is deleted in the machine destructor. The connection callback is called
380 in this state if it set. */
382 SILC_FSM_STATE(silc_client_connection_st_close)
384 SilcClientConnection conn = fsm_context;
385 SilcClientCommandContext cmd;
387 /* Finish running command threads. This will also finish waiting packet
388 thread, as they are always waiting for some command. If any thread is
389 waiting something else than command, they must be finished explicitly. */
390 if (silc_list_count(conn->internal->pending_commands)) {
391 SILC_LOG_DEBUG(("Finish pending commands"));
392 silc_list_start(conn->internal->pending_commands);
393 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
394 if (silc_fsm_is_started(&cmd->thread)) {
395 cmd->verbose = FALSE;
396 silc_fsm_continue_sync(&cmd->thread);
400 /* Give threads time to finish */
401 return SILC_FSM_YIELD;
404 /* Abort ongoing event */
405 if (conn->internal->op) {
406 SILC_LOG_DEBUG(("Abort event"));
407 silc_async_abort(conn->internal->op, NULL, NULL);
408 conn->internal->op = NULL;
411 /* If event thread is running, finish it. */
412 if (silc_fsm_is_started(&conn->internal->event_thread)) {
413 SILC_LOG_DEBUG(("Finish event thread"));
414 silc_fsm_continue_sync(&conn->internal->event_thread);
415 return SILC_FSM_YIELD;
418 /* Call the connection callback */
420 conn->callback(conn->client, conn, conn->internal->status,
421 conn->internal->error, conn->internal->disconnect_message,
422 conn->callback_context);
423 silc_free(conn->internal->disconnect_message);
425 SILC_LOG_DEBUG(("Closing remote connection"));
427 /* Close connection */
428 silc_packet_stream_destroy(conn->stream);
430 SILC_LOG_DEBUG(("Finishing connection machine"));
431 return SILC_FSM_FINISH;
434 /* Received error packet from server. Send it to application. */
436 SILC_FSM_STATE(silc_client_error)
438 SilcClientConnection conn = fsm_context;
439 SilcClient client = conn->client;
440 SilcPacket packet = state_context;
443 msg = silc_memdup(silc_buffer_data(&packet->buffer),
444 silc_buffer_len(&packet->buffer));
446 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
449 silc_packet_free(packet);
451 return SILC_FSM_FINISH;
454 /* Received disconnect packet from server. We close the connection and
455 send the disconnect message to application. */
457 SILC_FSM_STATE(silc_client_disconnect)
459 SilcClientConnection conn = fsm_context;
460 SilcPacket packet = state_context;
462 char *message = NULL;
464 SILC_LOG_DEBUG(("Server disconnected"));
466 if (silc_buffer_len(&packet->buffer) < 1) {
467 silc_packet_free(packet);
468 return SILC_FSM_FINISH;
471 status = (SilcStatus)packet->buffer.data[0];
473 silc_buffer_pull(&packet->buffer, 1);
474 if (silc_buffer_len(&packet->buffer) > 1 &&
475 silc_utf8_valid(silc_buffer_data(&packet->buffer),
476 silc_buffer_len(&packet->buffer)))
477 message = silc_memdup(silc_buffer_data(&packet->buffer),
478 silc_buffer_len(&packet->buffer));
480 /* Call connection callback */
481 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
482 conn->internal->error = status;
483 conn->internal->disconnect_message = message;
485 /* Signal to close connection */
486 if (!conn->internal->disconnected) {
487 conn->internal->disconnected = TRUE;
488 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
491 silc_packet_free(packet);
493 return SILC_FSM_FINISH;
496 /*************************** Main client machine ****************************/
498 /* The client's main state where we wait for various events */
500 SILC_FSM_STATE(silc_client_st_run)
502 SilcClient client = fsm_context;
504 /* Wait for events */
505 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
509 if (client->internal->run_callback && client->internal->running) {
510 /* Call running callbcak back to application */
511 SILC_LOG_DEBUG(("We are up, call running callback"));
512 client->internal->run_callback = FALSE;
513 client->internal->running(client, client->internal->running_context);
514 return SILC_FSM_CONTINUE;
517 if (client->internal->connection_closed) {
518 /* A connection finished */
519 SILC_LOG_DEBUG(("Event: connection closed"));
520 client->internal->connection_closed = FALSE;
521 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
522 client->internal->stop)
523 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
524 return SILC_FSM_CONTINUE;
527 if (client->internal->stop) {
528 /* Stop client libarry. If we have running connections, wait until
529 they finish first. */
530 SILC_LOG_DEBUG(("Event: stop"));
531 if (silc_atomic_get_int16(&client->internal->conns) == 0)
532 silc_fsm_next(fsm, silc_client_st_stop);
533 return SILC_FSM_CONTINUE;
538 return SILC_FSM_CONTINUE;
541 /* Stop event. Stops the client library. */
543 SILC_FSM_STATE(silc_client_st_stop)
545 SilcClient client = fsm_context;
547 SILC_LOG_DEBUG(("Client stopped"));
550 silc_schedule_stop(client->schedule);
551 silc_client_commands_unregister(client);
553 /* Call stopped callback to application */
554 if (client->internal->running)
555 client->internal->running(client, client->internal->running_context);
557 return SILC_FSM_FINISH;
560 /******************************* Private API ********************************/
562 /* Adds new connection. Creates the connection context and returns it. */
564 static SilcClientConnection
565 silc_client_add_connection(SilcClient client,
566 SilcConnectionType conn_type,
567 SilcClientConnectionParams *params,
568 SilcPublicKey public_key,
569 SilcPrivateKey private_key,
570 char *remote_host, int port,
571 SilcClientConnectCallback callback,
574 SilcClientConnection conn;
575 SilcFSMThread thread;
580 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
582 conn = silc_calloc(1, sizeof(*conn));
586 conn->client = client;
587 conn->public_key = public_key;
588 conn->private_key = private_key;
589 conn->remote_host = strdup(remote_host);
590 conn->remote_port = port ? port : 706;
591 conn->type = conn_type;
592 conn->callback = callback;
593 conn->callback_context = context;
595 conn->internal = silc_calloc(1, sizeof(*conn->internal));
596 if (!conn->internal) {
600 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
601 silc_mutex_alloc(&conn->internal->lock);
602 silc_atomic_init16(&conn->internal->cmd_ident, 0);
604 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
606 silc_free(conn->internal);
612 conn->internal->params = *params;
613 if (!conn->internal->params.rekey_secs)
614 conn->internal->params.rekey_secs = 3600;
615 #ifndef SILC_DIST_INPLACE
616 if (conn->internal->params.rekey_secs < 300)
617 conn->internal->params.rekey_secs = 300;
618 #endif /* SILC_DIST_INPLACE */
620 conn->internal->verbose = TRUE;
621 silc_list_init(conn->internal->pending_commands,
622 struct SilcClientCommandContextStruct, next);
623 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
625 /* Allocate client, channel and serve caches */
626 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
628 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
630 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
632 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
633 !conn->internal->server_cache) {
634 silc_client_del_connection(client, conn);
638 // conn->internal->ftp_sessions = silc_dlist_init();
640 /* Initialize our async operation so that application may abort us
641 while we're connecting. */
642 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
644 if (!conn->internal->cop) {
645 silc_client_del_connection(client, conn);
649 /* Run the connection state machine. If threads are in use the machine
650 is always run in a real thread. */
651 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
652 silc_client_connection_finished, NULL,
653 client->internal->params->threads);
655 silc_client_del_connection(client, conn);
658 silc_fsm_set_state_context(thread, client);
659 silc_fsm_start(thread, silc_client_connection_st_start);
661 SILC_LOG_DEBUG(("New connection %p", conn));
662 silc_atomic_add_int16(&client->internal->conns, 1);
667 /* Deletes connection. This is always called from the connection machine
668 destructor. Do not call this directly other places. */
670 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
673 SilcIDCacheEntry entry;
674 SilcFSMThread thread;
676 SILC_LOG_DEBUG(("Freeing connection %p", conn));
678 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
680 /* Free all cache entries */
681 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
682 silc_list_start(list);
683 while ((entry = silc_list_get(list)))
684 silc_client_del_server(client, conn, entry->context);
686 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
687 silc_list_start(list);
688 while ((entry = silc_list_get(list))) {
689 silc_client_empty_channel(client, conn, entry->context);
690 silc_client_del_channel(client, conn, entry->context);
693 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
694 silc_list_start(list);
695 while ((entry = silc_list_get(list)))
696 silc_client_del_client(client, conn, entry->context);
700 if (conn->internal->client_cache)
701 silc_idcache_free(conn->internal->client_cache);
702 if (conn->internal->channel_cache)
703 silc_idcache_free(conn->internal->channel_cache);
704 if (conn->internal->server_cache)
705 silc_idcache_free(conn->internal->server_cache);
707 /* Free thread pool */
708 silc_list_start(conn->internal->thread_pool);
709 while ((thread = silc_list_get(conn->internal->thread_pool)))
710 silc_fsm_free(thread);
712 silc_free(conn->remote_host);
713 silc_buffer_free(conn->internal->local_idp);
714 silc_buffer_free(conn->internal->remote_idp);
715 silc_mutex_free(conn->internal->lock);
716 if (conn->internal->hash)
717 silc_hash_free(conn->internal->hash);
718 if (conn->internal->sha1hash)
719 silc_hash_free(conn->internal->sha1hash);
720 silc_atomic_uninit16(&conn->internal->cmd_ident);
722 silc_free(conn->internal);
723 memset(conn, 'F', sizeof(*conn));
727 /******************************* Client API *********************************/
729 /* Connects to remote server. This is the main routine used to connect
730 to remote SILC server. Performs key exchange also. Returns the
731 connection context to the connection callback. */
734 silc_client_connect_to_server(SilcClient client,
735 SilcClientConnectionParams *params,
736 SilcPublicKey public_key,
737 SilcPrivateKey private_key,
738 char *remote_host, int port,
739 SilcClientConnectCallback callback,
742 SilcClientConnection conn;
744 SILC_LOG_DEBUG(("Connecting to server"));
746 if (!client || !remote_host)
749 /* Add new connection */
750 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
751 public_key, private_key, remote_host,
752 port, callback, context);
754 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
758 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
759 "Connecting to port %d of server %s",
762 /* Signal connection machine to start connecting */
763 conn->internal->connect = TRUE;
764 return conn->internal->cop;
767 /* Connects to remote client. Performs key exchange also. Returns the
768 connection context to the connection callback. */
771 silc_client_connect_to_client(SilcClient client,
772 SilcClientConnectionParams *params,
773 SilcPublicKey public_key,
774 SilcPrivateKey private_key,
775 char *remote_host, int port,
776 SilcClientConnectCallback callback,
779 SilcClientConnection conn;
781 SILC_LOG_DEBUG(("Connecting to client"));
783 if (!client || !remote_host)
786 /* Add new connection */
787 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
788 public_key, private_key, remote_host,
789 port, callback, context);
791 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
795 /* Signal connection machine to start connecting */
796 conn->internal->connect = TRUE;
797 return conn->internal->cop;
800 /* Starts key exchange in the remote stream indicated by `stream'. This
801 creates the connection context and returns it in the connection callback. */
804 silc_client_key_exchange(SilcClient client,
805 SilcClientConnectionParams *params,
806 SilcPublicKey public_key,
807 SilcPrivateKey private_key,
809 SilcConnectionType conn_type,
810 SilcClientConnectCallback callback,
813 SilcClientConnection conn;
817 SILC_LOG_DEBUG(("Performing key exchange"));
819 if (!client || !stream)
822 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
823 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
824 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
828 /* Add new connection */
829 conn = silc_client_add_connection(client, conn_type, params,
830 public_key, private_key,
831 (char *)host, port, callback, context);
833 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
836 conn->stream = (void *)stream;
838 /* Signal connection to start key exchange */
839 conn->internal->key_exchange = TRUE;
840 return conn->internal->cop;
843 /* Closes remote connection */
845 void silc_client_close_connection(SilcClient client,
846 SilcClientConnection conn)
848 SILC_LOG_DEBUG(("Closing connection %p", conn));
850 /* Signal to close connection */
851 if (!conn->internal->disconnected) {
852 conn->internal->disconnected = TRUE;
853 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
857 /* Allocates new client object. This has to be done before client may
858 work. After calling this one must call silc_client_init to initialize
859 the client. The `application' is application specific user data pointer
860 and caller must free it. */
862 SilcClient silc_client_alloc(SilcClientOperations *ops,
863 SilcClientParams *params,
865 const char *version_string)
867 SilcClient new_client;
869 new_client = silc_calloc(1, sizeof(*new_client));
872 new_client->application = application;
874 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
875 if (!new_client->internal) {
876 silc_free(new_client);
879 new_client->internal->ops = ops;
880 new_client->internal->params =
881 silc_calloc(1, sizeof(*new_client->internal->params));
883 version_string = silc_version_string;
884 new_client->internal->silc_client_version = strdup(version_string);
887 memcpy(new_client->internal->params, params, sizeof(*params));
889 new_client->internal->params->
890 nickname_format[sizeof(new_client->internal->
891 params->nickname_format) - 1] = 0;
893 silc_atomic_init16(&new_client->internal->conns, 0);
898 /* Frees client object and its internals. */
900 void silc_client_free(SilcClient client)
902 silc_schedule_uninit(client->schedule);
905 silc_rng_free(client->rng);
907 if (!client->internal->params->dont_register_crypto_library) {
908 silc_cipher_unregister_all();
909 silc_pkcs_unregister_all();
910 silc_hash_unregister_all();
911 silc_hmac_unregister_all();
914 silc_atomic_uninit16(&client->internal->conns);
915 silc_free(client->username);
916 silc_free(client->hostname);
917 silc_free(client->realname);
918 silc_free(client->internal->params);
919 silc_free(client->internal->silc_client_version);
920 silc_free(client->internal);
924 /* Initializes the client. This makes all the necessary steps to make
925 the client ready to be run. One must call silc_client_run to run the
926 client. Returns FALSE if error occured, TRUE otherwise. */
928 SilcBool silc_client_init(SilcClient client, const char *username,
929 const char *hostname, const char *realname,
930 SilcClientRunning running, void *context)
932 SILC_LOG_DEBUG(("Initializing client"));
937 if (!username || !hostname) {
938 SILC_LOG_ERROR(("Username and hostname must be given to "
939 "silc_client_init"));
945 /* Validate essential strings */
946 if (!silc_identifier_verify(username, strlen(username),
947 SILC_STRING_UTF8, 128)) {
948 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
952 if (!silc_identifier_verify(hostname, strlen(hostname),
953 SILC_STRING_UTF8, 256)) {
954 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
958 if (!silc_utf8_valid(realname, strlen(realname))) {
959 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
964 /* Take the name strings */
965 client->username = strdup(username);
966 client->hostname = strdup(hostname);
967 client->realname = strdup(realname);
968 if (!username || !hostname || !realname)
971 if (!client->internal->params->dont_register_crypto_library) {
972 /* Initialize the crypto library. If application has done this already
973 this has no effect. Also, we will not be overriding something
974 application might have registered earlier. */
975 silc_cipher_register_default();
976 silc_pkcs_register_default();
977 silc_hash_register_default();
978 silc_hmac_register_default();
981 /* Initialize random number generator */
982 client->rng = silc_rng_alloc();
985 silc_rng_init(client->rng);
986 silc_rng_global_init(client->rng);
988 /* Initialize the scheduler */
989 client->schedule = silc_schedule_init(0, client);
990 if (!client->schedule)
993 /* Allocate client lock */
994 silc_mutex_alloc(&client->internal->lock);
996 /* Register commands */
997 silc_client_commands_register(client);
999 /* Start packet engine */
1000 client->internal->packet_engine =
1001 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1003 if (!client->internal->packet_engine)
1006 /* Initialize and start the client FSM */
1007 client->internal->running = running;
1008 client->internal->running_context = context;
1009 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1010 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1011 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1013 /* Signal the application when we are running */
1014 client->internal->run_callback = TRUE;
1015 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1020 /* Starts the SILC client FSM machine and blocks here. When this returns
1021 the client has ended. */
1023 void silc_client_run(SilcClient client)
1025 SILC_LOG_DEBUG(("Starting SILC client"));
1027 /* Run the scheduler */
1028 silc_schedule(client->schedule);
1031 /* Call scheduler one iteration and return. */
1033 void silc_client_run_one(SilcClient client)
1035 if (silc_fsm_is_started(&client->internal->fsm))
1036 silc_schedule_one(client->schedule, 0);
1039 /* Stops the client. This is called to stop the client and thus to stop
1042 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1045 SILC_LOG_DEBUG(("Stopping client"));
1047 client->internal->running = (SilcClientRunning)stopped;
1048 client->internal->running_context = context;
1050 /* Signal to stop */
1051 client->internal->stop = TRUE;
1052 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);