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;
135 SilcClient client = conn->client;
137 SILC_LOG_DEBUG(("Remote disconnected connection"));
139 /* Call connection callback */
140 if (!conn->internal->callback_called)
141 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, 0, NULL,
142 conn->callback_context);
143 conn->internal->callback_called = TRUE;
145 /* Signal to close connection */
146 if (!conn->internal->disconnected) {
147 conn->internal->disconnected = TRUE;
148 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
152 /* Packet engine callback to indicate error */
154 static void silc_client_packet_error(SilcPacketEngine engine,
155 SilcPacketStream stream,
156 SilcPacketError error,
157 void *callback_context,
158 void *stream_context)
163 /* Packet stream callbacks */
164 static SilcPacketCallbacks silc_client_stream_cbs =
166 silc_client_packet_receive,
167 silc_client_packet_eos,
168 silc_client_packet_error
173 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
174 void *destructor_context)
179 /* Connect abort operation */
181 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
183 SilcClientConnection conn = context;
185 SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
187 /* Signal to close connection */
188 if (!conn->internal->disconnected) {
189 conn->internal->disconnected = TRUE;
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 set in initialization */
215 if (conn->internal->connect)
216 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
217 if (conn->internal->key_exchange)
218 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
220 /* Wait until this thread is terminated from the machine destructor */
221 return SILC_FSM_WAIT;
224 /* Connection machine main state. This handles various connection related
225 events, but not packet processing. It's done in dedicated packet
226 processing FSM thread. */
228 SILC_FSM_STATE(silc_client_connection_st_run)
230 SilcClientConnection conn = fsm_context;
231 SilcFSMThread thread;
233 /* Wait for events */
234 SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
237 thread = &conn->internal->event_thread;
239 if (conn->internal->connect) {
240 SILC_LOG_DEBUG(("Event: connect"));
241 conn->internal->connect = FALSE;
243 /*** Event: connect */
244 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
246 silc_fsm_start_sync(thread, silc_client_st_connect);
247 return SILC_FSM_CONTINUE;
250 if (conn->internal->key_exchange) {
251 SILC_LOG_DEBUG(("Event: key exchange"));
252 conn->internal->key_exchange = FALSE;
254 /*** Event: key exchange */
255 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
257 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
258 return SILC_FSM_CONTINUE;
261 if (conn->internal->rekeying) {
262 SILC_LOG_DEBUG(("Event: rekey"));
263 conn->internal->rekeying = FALSE;
266 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
268 silc_fsm_start_sync(thread, silc_client_st_rekey);
269 return SILC_FSM_CONTINUE;
272 if (conn->internal->disconnected) {
273 /** Event: disconnected */
274 SILC_LOG_DEBUG(("Event: disconnected"));
275 silc_fsm_next(fsm, silc_client_connection_st_close);
276 return SILC_FSM_YIELD;
281 return SILC_FSM_CONTINUE;
284 /* Packet processor thread. Each incoming packet is processed in FSM
285 thread in this state. The thread is run in the connection machine. */
287 SILC_FSM_STATE(silc_client_connection_st_packet)
289 SilcClientConnection conn = fsm_context;
290 SilcPacket packet = state_context;
292 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
294 switch (packet->type) {
296 case SILC_PACKET_PRIVATE_MESSAGE:
297 /** Private message */
298 silc_fsm_next(fsm, silc_client_private_message);
301 case SILC_PACKET_CHANNEL_MESSAGE:
302 /** Channel message */
303 silc_fsm_next(fsm, silc_client_channel_message);
306 case SILC_PACKET_FTP:
307 /* File transfer packet */
308 // silc_fsm_next(fsm, silc_client_ftp);
311 case SILC_PACKET_CHANNEL_KEY:
313 silc_fsm_next(fsm, silc_client_channel_key);
316 case SILC_PACKET_COMMAND_REPLY:
318 silc_fsm_next(fsm, silc_client_command_reply);
321 case SILC_PACKET_NOTIFY:
323 silc_fsm_next(fsm, silc_client_notify);
326 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
327 /* Private message key indicator */
328 silc_fsm_next(fsm, silc_client_private_message_key);
331 case SILC_PACKET_DISCONNECT:
333 silc_fsm_next(fsm, silc_client_disconnect);
336 case SILC_PACKET_ERROR:
337 /* Error by server */
338 silc_fsm_next(fsm, silc_client_error);
341 case SILC_PACKET_KEY_AGREEMENT:
343 silc_fsm_next(fsm, silc_client_key_agreement);
346 case SILC_PACKET_COMMAND:
347 /** Command packet */
348 silc_fsm_next(fsm, silc_client_command);
351 case SILC_PACKET_NEW_ID:
353 silc_fsm_next(fsm, silc_client_new_id);
356 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
357 /** Connection auth resolve reply */
358 silc_fsm_next(fsm, silc_client_connect_auth_request);
361 case SILC_PACKET_REKEY:
362 /* Signal to start rekey */
363 conn->internal->rekey_responder = TRUE;
364 conn->internal->rekeying = TRUE;
365 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
367 silc_packet_free(packet);
368 return SILC_FSM_FINISH;
372 silc_packet_free(packet);
373 return SILC_FSM_FINISH;
377 return SILC_FSM_CONTINUE;
380 /* Disconnection event to close remote connection. We close the connection
381 and finish the connection machine in this state. The connection context
382 is deleted in the machine destructor. The connection callback must be
383 already called back to application before getting here. */
385 SILC_FSM_STATE(silc_client_connection_st_close)
387 SilcClientConnection conn = fsm_context;
388 SilcClientCommandContext cmd;
390 /* Finish running command threads. This will also finish waiting packet
391 thread, as they are always waiting for some command. If any thread is
392 waiting something else than command, they must be finished explicitly. */
393 if (silc_list_count(conn->internal->pending_commands)) {
394 SILC_LOG_DEBUG(("Finish pending commands"));
395 silc_list_start(conn->internal->pending_commands);
396 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
397 if (silc_fsm_is_started(&cmd->thread)) {
398 cmd->verbose = FALSE;
399 silc_fsm_continue_sync(&cmd->thread);
403 /* Give threads time to finish */
404 return SILC_FSM_YIELD;
407 /* Abort ongoing event */
408 if (conn->internal->op) {
409 SILC_LOG_DEBUG(("Abort event"));
410 silc_async_abort(conn->internal->op, NULL, NULL);
411 conn->internal->op = NULL;
414 /* If event thread is running, finish it. */
415 if (silc_fsm_is_started(&conn->internal->event_thread)) {
416 SILC_LOG_DEBUG(("Finish event thread"));
417 silc_fsm_continue_sync(&conn->internal->event_thread);
418 return SILC_FSM_YIELD;
421 SILC_LOG_DEBUG(("Closing remote connection"));
423 /* Close connection */
424 silc_packet_stream_destroy(conn->stream);
426 SILC_LOG_DEBUG(("Finishing connection machine"));
427 return SILC_FSM_FINISH;
430 /* Received error packet from server. Send it to application. */
432 SILC_FSM_STATE(silc_client_error)
434 SilcClientConnection conn = fsm_context;
435 SilcClient client = conn->client;
436 SilcPacket packet = state_context;
439 msg = silc_memdup(silc_buffer_data(&packet->buffer),
440 silc_buffer_len(&packet->buffer));
442 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
445 silc_packet_free(packet);
447 return SILC_FSM_FINISH;
450 /* Received disconnect packet from server. We close the connection and
451 send the disconnect message to application. */
453 SILC_FSM_STATE(silc_client_disconnect)
455 SilcClientConnection conn = fsm_context;
456 SilcClient client = conn->client;
457 SilcPacket packet = state_context;
459 char *message = NULL;
461 SILC_LOG_DEBUG(("Server disconnected"));
463 if (silc_buffer_len(&packet->buffer) < 1) {
464 silc_packet_free(packet);
465 return SILC_FSM_FINISH;
468 status = (SilcStatus)packet->buffer.data[0];
470 silc_buffer_pull(&packet->buffer, 1);
471 if (silc_buffer_len(&packet->buffer) > 1 &&
472 silc_utf8_valid(silc_buffer_data(&packet->buffer),
473 silc_buffer_len(&packet->buffer)))
474 message = silc_memdup(silc_buffer_data(&packet->buffer),
475 silc_buffer_len(&packet->buffer));
477 /* Call connection callback */
478 if (!conn->internal->callback_called)
479 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
480 message, conn->callback_context);
481 conn->internal->callback_called = TRUE;
484 silc_packet_free(packet);
486 /* Signal to close connection */
487 if (!conn->internal->disconnected) {
488 conn->internal->disconnected = TRUE;
489 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
492 return SILC_FSM_FINISH;
495 /*************************** Main client machine ****************************/
497 /* The client's main state where we wait for various events */
499 SILC_FSM_STATE(silc_client_st_run)
501 SilcClient client = fsm_context;
503 /* Wait for events */
504 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
508 if (client->internal->run_callback && client->internal->running) {
509 /* Call running callbcak back to application */
510 SILC_LOG_DEBUG(("We are up, call running callback"));
511 client->internal->run_callback = FALSE;
512 client->internal->running(client, client->internal->running_context);
513 return SILC_FSM_CONTINUE;
516 if (client->internal->connection_closed) {
517 /* A connection finished */
518 SILC_LOG_DEBUG(("Event: connection closed"));
519 client->internal->connection_closed = FALSE;
520 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
521 client->internal->stop)
522 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
523 return SILC_FSM_CONTINUE;
526 if (client->internal->stop) {
527 /* Stop client libarry. If we have running connections, wait until
528 they finish first. */
529 SILC_LOG_DEBUG(("Event: stop"));
530 if (silc_atomic_get_int16(&client->internal->conns) == 0)
531 silc_fsm_next(fsm, silc_client_st_stop);
532 return SILC_FSM_CONTINUE;
537 return SILC_FSM_CONTINUE;
540 /* Stop event. Stops the client library. */
542 SILC_FSM_STATE(silc_client_st_stop)
544 SilcClient client = fsm_context;
546 SILC_LOG_DEBUG(("Client stopped"));
549 silc_schedule_stop(client->schedule);
550 silc_client_commands_unregister(client);
552 /* Call stopped callback to application */
553 if (client->internal->running)
554 client->internal->running(client, client->internal->running_context);
556 return SILC_FSM_FINISH;
559 /******************************* Private API ********************************/
561 /* Adds new connection. Creates the connection context and returns it. */
563 static SilcClientConnection
564 silc_client_add_connection(SilcClient client,
565 SilcConnectionType conn_type,
566 SilcClientConnectionParams *params,
567 SilcPublicKey public_key,
568 SilcPrivateKey private_key,
569 char *remote_host, int port,
570 SilcClientConnectCallback callback,
573 SilcClientConnection conn;
574 SilcFSMThread thread;
579 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
581 conn = silc_calloc(1, sizeof(*conn));
585 conn->client = client;
586 conn->public_key = public_key;
587 conn->private_key = private_key;
588 conn->remote_host = strdup(remote_host);
589 conn->remote_port = port ? port : 706;
590 conn->type = conn_type;
591 conn->callback = callback;
592 conn->callback_context = context;
594 conn->internal = silc_calloc(1, sizeof(*conn->internal));
595 if (!conn->internal) {
599 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
600 silc_mutex_alloc(&conn->internal->lock);
601 silc_atomic_init16(&conn->internal->cmd_ident, 0);
603 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
605 silc_free(conn->internal);
611 conn->internal->params = *params;
612 if (!conn->internal->params.rekey_secs)
613 conn->internal->params.rekey_secs = 3600;
614 #ifndef SILC_DIST_INPLACE
615 if (conn->internal->params.rekey_secs < 300)
616 conn->internal->params.rekey_secs = 300;
617 #endif /* SILC_DIST_INPLACE */
619 conn->internal->verbose = TRUE;
620 silc_list_init(conn->internal->pending_commands,
621 struct SilcClientCommandContextStruct, next);
622 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
624 /* Allocate client, channel and serve caches */
625 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
627 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
629 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
631 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
632 !conn->internal->server_cache) {
633 silc_client_del_connection(client, conn);
637 // conn->internal->ftp_sessions = silc_dlist_init();
639 /* Initialize our async operation so that application may abort us
640 while we're connecting. */
641 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
643 if (!conn->internal->cop) {
644 silc_client_del_connection(client, conn);
648 /* Run the connection state machine. If threads are in use the machine
649 is always run in a real thread. */
650 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
651 silc_client_connection_finished, NULL,
652 client->internal->params->threads);
654 silc_client_del_connection(client, conn);
657 silc_fsm_set_state_context(thread, client);
658 silc_fsm_start(thread, silc_client_connection_st_start);
660 SILC_LOG_DEBUG(("New connection %p", conn));
661 silc_atomic_add_int16(&client->internal->conns, 1);
666 /* Deletes connection. This is always called from the connection machine
667 destructor. Do not call this directly other places. */
669 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
672 SilcIDCacheEntry entry;
673 SilcFSMThread thread;
675 SILC_LOG_DEBUG(("Freeing connection %p", conn));
677 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
679 /* Free all cache entries */
680 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
681 silc_list_start(list);
682 while ((entry = silc_list_get(list)))
683 silc_client_del_server(client, conn, entry->context);
685 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
686 silc_list_start(list);
687 while ((entry = silc_list_get(list))) {
688 silc_client_empty_channel(client, conn, entry->context);
689 silc_client_del_channel(client, conn, entry->context);
692 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
693 silc_list_start(list);
694 while ((entry = silc_list_get(list)))
695 silc_client_del_client(client, conn, entry->context);
699 if (conn->internal->client_cache)
700 silc_idcache_free(conn->internal->client_cache);
701 if (conn->internal->channel_cache)
702 silc_idcache_free(conn->internal->channel_cache);
703 if (conn->internal->server_cache)
704 silc_idcache_free(conn->internal->server_cache);
706 /* Free thread pool */
707 silc_list_start(conn->internal->thread_pool);
708 while ((thread = silc_list_get(conn->internal->thread_pool)))
709 silc_fsm_free(thread);
711 silc_free(conn->remote_host);
712 silc_buffer_free(conn->internal->local_idp);
713 silc_buffer_free(conn->internal->remote_idp);
714 silc_mutex_free(conn->internal->lock);
715 if (conn->internal->hash)
716 silc_hash_free(conn->internal->hash);
717 if (conn->internal->sha1hash)
718 silc_hash_free(conn->internal->sha1hash);
719 silc_atomic_uninit16(&conn->internal->cmd_ident);
721 silc_free(conn->internal);
722 memset(conn, 'F', sizeof(*conn));
726 /******************************* Client API *********************************/
728 /* Connects to remote server. This is the main routine used to connect
729 to remote SILC server. Performs key exchange also. Returns the
730 connection context to the connection callback. */
733 silc_client_connect_to_server(SilcClient client,
734 SilcClientConnectionParams *params,
735 SilcPublicKey public_key,
736 SilcPrivateKey private_key,
737 char *remote_host, int port,
738 SilcClientConnectCallback callback,
741 SilcClientConnection conn;
743 SILC_LOG_DEBUG(("Connecting to server"));
745 if (!client || !remote_host)
748 /* Add new connection */
749 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
750 public_key, private_key, remote_host,
751 port, callback, context);
753 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
757 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
758 "Connecting to port %d of server %s",
761 /* Signal connection machine to start connecting */
762 conn->internal->connect = TRUE;
763 return conn->internal->cop;
766 /* Connects to remote client. Performs key exchange also. Returns the
767 connection context to the connection callback. */
770 silc_client_connect_to_client(SilcClient client,
771 SilcClientConnectionParams *params,
772 SilcPublicKey public_key,
773 SilcPrivateKey private_key,
774 char *remote_host, int port,
775 SilcClientConnectCallback callback,
778 SilcClientConnection conn;
780 SILC_LOG_DEBUG(("Connecting to client"));
782 if (!client || !remote_host)
785 /* Add new connection */
786 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
787 public_key, private_key, remote_host,
788 port, callback, context);
790 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
794 /* Signal connection machine to start connecting */
795 conn->internal->connect = TRUE;
796 return conn->internal->cop;
799 /* Starts key exchange in the remote stream indicated by `stream'. This
800 creates the connection context and returns it in the connection callback. */
803 silc_client_key_exchange(SilcClient client,
804 SilcClientConnectionParams *params,
805 SilcPublicKey public_key,
806 SilcPrivateKey private_key,
808 SilcConnectionType conn_type,
809 SilcClientConnectCallback callback,
812 SilcClientConnection conn;
816 SILC_LOG_DEBUG(("Performing key exchange"));
818 if (!client || !stream)
821 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
822 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
823 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
827 /* Add new connection */
828 conn = silc_client_add_connection(client, conn_type, params,
829 public_key, private_key,
830 (char *)host, port, callback, context);
832 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
835 conn->stream = (void *)stream;
837 /* Signal connection to start key exchange */
838 conn->internal->key_exchange = TRUE;
839 return conn->internal->cop;
842 /* Closes remote connection */
844 void silc_client_close_connection(SilcClient client,
845 SilcClientConnection conn)
847 SILC_LOG_DEBUG(("Closing connection %p", conn));
849 /* Signal to close connection */
850 if (!conn->internal->disconnected) {
851 conn->internal->disconnected = TRUE;
852 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
856 /* Allocates new client object. This has to be done before client may
857 work. After calling this one must call silc_client_init to initialize
858 the client. The `application' is application specific user data pointer
859 and caller must free it. */
861 SilcClient silc_client_alloc(SilcClientOperations *ops,
862 SilcClientParams *params,
864 const char *version_string)
866 SilcClient new_client;
868 new_client = silc_calloc(1, sizeof(*new_client));
871 new_client->application = application;
873 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
874 if (!new_client->internal) {
875 silc_free(new_client);
878 new_client->internal->ops = ops;
879 new_client->internal->params =
880 silc_calloc(1, sizeof(*new_client->internal->params));
882 version_string = silc_version_string;
883 new_client->internal->silc_client_version = strdup(version_string);
886 memcpy(new_client->internal->params, params, sizeof(*params));
888 new_client->internal->params->
889 nickname_format[sizeof(new_client->internal->
890 params->nickname_format) - 1] = 0;
892 silc_atomic_init16(&new_client->internal->conns, 0);
897 /* Frees client object and its internals. */
899 void silc_client_free(SilcClient client)
901 silc_schedule_uninit(client->schedule);
904 silc_rng_free(client->rng);
906 if (!client->internal->params->dont_register_crypto_library) {
907 silc_cipher_unregister_all();
908 silc_pkcs_unregister_all();
909 silc_hash_unregister_all();
910 silc_hmac_unregister_all();
913 silc_atomic_uninit16(&client->internal->conns);
914 silc_free(client->username);
915 silc_free(client->hostname);
916 silc_free(client->realname);
917 silc_free(client->internal->params);
918 silc_free(client->internal->silc_client_version);
919 silc_free(client->internal);
923 /* Initializes the client. This makes all the necessary steps to make
924 the client ready to be run. One must call silc_client_run to run the
925 client. Returns FALSE if error occured, TRUE otherwise. */
927 SilcBool silc_client_init(SilcClient client, const char *username,
928 const char *hostname, const char *realname,
929 SilcClientRunning running, void *context)
931 SILC_LOG_DEBUG(("Initializing client"));
936 if (!username || !hostname) {
937 SILC_LOG_ERROR(("Username and hostname must be given to "
938 "silc_client_init"));
944 /* Validate essential strings */
945 if (!silc_identifier_verify(username, strlen(username),
946 SILC_STRING_UTF8, 128)) {
947 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
951 if (!silc_identifier_verify(hostname, strlen(hostname),
952 SILC_STRING_UTF8, 256)) {
953 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
957 if (!silc_utf8_valid(realname, strlen(realname))) {
958 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
963 /* Take the name strings */
964 client->username = strdup(username);
965 client->hostname = strdup(hostname);
966 client->realname = strdup(realname);
967 if (!username || !hostname || !realname)
970 if (!client->internal->params->dont_register_crypto_library) {
971 /* Initialize the crypto library. If application has done this already
972 this has no effect. Also, we will not be overriding something
973 application might have registered earlier. */
974 silc_cipher_register_default();
975 silc_pkcs_register_default();
976 silc_hash_register_default();
977 silc_hmac_register_default();
980 /* Initialize random number generator */
981 client->rng = silc_rng_alloc();
984 silc_rng_init(client->rng);
985 silc_rng_global_init(client->rng);
987 /* Initialize the scheduler */
988 client->schedule = silc_schedule_init(0, client);
989 if (!client->schedule)
992 /* Allocate client lock */
993 silc_mutex_alloc(&client->internal->lock);
995 /* Register commands */
996 silc_client_commands_register(client);
998 /* Start packet engine */
999 client->internal->packet_engine =
1000 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1002 if (!client->internal->packet_engine)
1005 /* Initialize and start the client FSM */
1006 client->internal->running = running;
1007 client->internal->running_context = context;
1008 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1009 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1010 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1012 /* Signal the application when we are running */
1013 client->internal->run_callback = TRUE;
1014 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1019 /* Starts the SILC client FSM machine and blocks here. When this returns
1020 the client has ended. */
1022 void silc_client_run(SilcClient client)
1024 SILC_LOG_DEBUG(("Starting SILC client"));
1026 /* Run the scheduler */
1027 silc_schedule(client->schedule);
1030 /* Call scheduler one iteration and return. */
1032 void silc_client_run_one(SilcClient client)
1034 if (silc_fsm_is_started(&client->internal->fsm))
1035 silc_schedule_one(client->schedule, 0);
1038 /* Stops the client. This is called to stop the client and thus to stop
1041 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1044 SILC_LOG_DEBUG(("Stopping client"));
1046 client->internal->running = (SilcClientRunning)stopped;
1047 client->internal->running_context = context;
1049 /* Signal to stop */
1050 client->internal->stop = TRUE;
1051 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);