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;
239 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
241 /*** Event: connect */
242 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
244 silc_fsm_start_sync(thread, silc_client_st_connect);
245 return SILC_FSM_CONTINUE;
248 if (conn->internal->key_exchange) {
249 SILC_LOG_DEBUG(("Event: key exchange"));
250 conn->internal->key_exchange = FALSE;
251 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
253 /*** Event: key exchange */
254 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
256 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
257 return SILC_FSM_CONTINUE;
260 if (conn->internal->rekeying) {
261 SILC_LOG_DEBUG(("Event: rekey"));
262 conn->internal->rekeying = FALSE;
263 SILC_ASSERT(silc_fsm_is_started(thread) == 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 is called
383 in this state if it is set. */
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 /* Call the connection callback */
423 conn->callback(conn->client, conn, conn->internal->status,
424 conn->internal->error, conn->internal->disconnect_message,
425 conn->callback_context);
426 silc_free(conn->internal->disconnect_message);
428 SILC_LOG_DEBUG(("Closing remote connection"));
430 /* Close connection */
431 silc_packet_stream_destroy(conn->stream);
433 SILC_LOG_DEBUG(("Finishing connection machine"));
434 return SILC_FSM_FINISH;
437 /* Received error packet from server. Send it to application. */
439 SILC_FSM_STATE(silc_client_error)
441 SilcClientConnection conn = fsm_context;
442 SilcClient client = conn->client;
443 SilcPacket packet = state_context;
446 msg = silc_memdup(silc_buffer_data(&packet->buffer),
447 silc_buffer_len(&packet->buffer));
449 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
452 silc_packet_free(packet);
454 return SILC_FSM_FINISH;
457 /* Received disconnect packet from server. We close the connection and
458 send the disconnect message to application. */
460 SILC_FSM_STATE(silc_client_disconnect)
462 SilcClientConnection conn = fsm_context;
463 SilcPacket packet = state_context;
465 char *message = NULL;
467 SILC_LOG_DEBUG(("Server disconnected"));
469 if (silc_buffer_len(&packet->buffer) < 1) {
470 silc_packet_free(packet);
471 return SILC_FSM_FINISH;
474 status = (SilcStatus)packet->buffer.data[0];
476 silc_buffer_pull(&packet->buffer, 1);
477 if (silc_buffer_len(&packet->buffer) > 1 &&
478 silc_utf8_valid(silc_buffer_data(&packet->buffer),
479 silc_buffer_len(&packet->buffer)))
480 message = silc_memdup(silc_buffer_data(&packet->buffer),
481 silc_buffer_len(&packet->buffer));
483 /* Call connection callback */
484 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
485 conn->internal->error = status;
486 conn->internal->disconnect_message = message;
488 /* Signal to close connection */
489 if (!conn->internal->disconnected) {
490 conn->internal->disconnected = TRUE;
491 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
494 silc_packet_free(packet);
496 return SILC_FSM_FINISH;
499 /*************************** Main client machine ****************************/
501 /* The client's main state where we wait for various events */
503 SILC_FSM_STATE(silc_client_st_run)
505 SilcClient client = fsm_context;
507 /* Wait for events */
508 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
512 if (client->internal->run_callback && client->internal->running) {
513 /* Call running callbcak back to application */
514 SILC_LOG_DEBUG(("We are up, call running callback"));
515 client->internal->run_callback = FALSE;
516 client->internal->running(client, client->internal->running_context);
517 return SILC_FSM_CONTINUE;
520 if (client->internal->connection_closed) {
521 /* A connection finished */
522 SILC_LOG_DEBUG(("Event: connection closed"));
523 client->internal->connection_closed = FALSE;
524 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
525 client->internal->stop)
526 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
527 return SILC_FSM_CONTINUE;
530 if (client->internal->stop) {
531 /* Stop client libarry. If we have running connections, wait until
532 they finish first. */
533 SILC_LOG_DEBUG(("Event: stop"));
534 if (silc_atomic_get_int16(&client->internal->conns) == 0)
535 silc_fsm_next(fsm, silc_client_st_stop);
536 return SILC_FSM_CONTINUE;
541 return SILC_FSM_CONTINUE;
544 /* Stop event. Stops the client library. */
546 SILC_FSM_STATE(silc_client_st_stop)
548 SilcClient client = fsm_context;
550 SILC_LOG_DEBUG(("Client stopped"));
553 silc_schedule_stop(client->schedule);
554 silc_client_commands_unregister(client);
556 /* Call stopped callback to application */
557 if (client->internal->running)
558 client->internal->running(client, client->internal->running_context);
560 return SILC_FSM_FINISH;
563 /******************************* Private API ********************************/
565 /* Adds new connection. Creates the connection context and returns it. */
568 silc_client_add_connection(SilcClient client,
569 SilcConnectionType conn_type,
571 SilcClientConnectionParams *params,
572 SilcPublicKey public_key,
573 SilcPrivateKey private_key,
574 char *remote_host, int port,
575 SilcClientConnectCallback callback,
578 SilcClientConnection conn;
579 SilcFSMThread thread;
584 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
586 conn = silc_calloc(1, sizeof(*conn));
590 conn->client = client;
591 conn->public_key = public_key;
592 conn->private_key = private_key;
593 conn->remote_host = strdup(remote_host);
594 conn->remote_port = port ? port : 706;
595 conn->type = conn_type;
596 conn->callback = callback;
597 conn->callback_context = context;
599 conn->internal = silc_calloc(1, sizeof(*conn->internal));
600 if (!conn->internal) {
604 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
605 silc_mutex_alloc(&conn->internal->lock);
606 silc_atomic_init16(&conn->internal->cmd_ident, 0);
608 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
610 silc_free(conn->internal);
616 conn->internal->params = *params;
617 if (!conn->internal->params.rekey_secs)
618 conn->internal->params.rekey_secs = 3600;
619 #ifndef SILC_DIST_INPLACE
620 if (conn->internal->params.rekey_secs < 300)
621 conn->internal->params.rekey_secs = 300;
622 #endif /* SILC_DIST_INPLACE */
624 conn->internal->verbose = TRUE;
625 silc_list_init(conn->internal->pending_commands,
626 struct SilcClientCommandContextStruct, next);
627 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
629 /* Allocate client, channel and serve caches */
630 if (conn_type != SILC_CONN_CLIENT) {
631 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
633 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
635 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
637 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
638 !conn->internal->server_cache) {
639 silc_client_del_connection(client, conn);
645 /* Initialize our async operation so that application may abort us
646 while we're connecting. */
647 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
649 if (!conn->internal->cop) {
650 silc_client_del_connection(client, conn);
655 /* Run the connection state machine. If threads are in use the connection
656 machine is always run in a real thread. */
657 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
658 silc_client_connection_finished, NULL,
659 client->internal->params->threads);
661 silc_client_del_connection(client, conn);
664 silc_fsm_set_state_context(thread, client);
665 silc_fsm_start(thread, silc_client_connection_st_start);
667 SILC_LOG_DEBUG(("New connection %p", conn));
668 silc_atomic_add_int16(&client->internal->conns, 1);
673 /* Deletes connection. This is always called from the connection machine
674 destructor. Do not call this directly other places. */
676 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
679 SilcIDCacheEntry entry;
680 SilcFSMThread thread;
682 SILC_LOG_DEBUG(("Freeing connection %p", conn));
684 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
686 /* Free all cache entries */
687 if (conn->internal->server_cache) {
688 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
689 silc_list_start(list);
690 while ((entry = silc_list_get(list)))
691 silc_client_del_server(client, conn, entry->context);
694 if (conn->internal->channel_cache) {
695 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
696 silc_list_start(list);
697 while ((entry = silc_list_get(list))) {
698 silc_client_empty_channel(client, conn, entry->context);
699 silc_client_del_channel(client, conn, entry->context);
703 if (conn->internal->client_cache) {
704 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
705 silc_list_start(list);
706 while ((entry = silc_list_get(list)))
707 silc_client_del_client(client, conn, entry->context);
712 if (conn->internal->client_cache)
713 silc_idcache_free(conn->internal->client_cache);
714 if (conn->internal->channel_cache)
715 silc_idcache_free(conn->internal->channel_cache);
716 if (conn->internal->server_cache)
717 silc_idcache_free(conn->internal->server_cache);
719 /* Free thread pool */
720 silc_list_start(conn->internal->thread_pool);
721 while ((thread = silc_list_get(conn->internal->thread_pool)))
722 silc_fsm_free(thread);
724 silc_free(conn->remote_host);
725 silc_buffer_free(conn->internal->local_idp);
726 silc_buffer_free(conn->internal->remote_idp);
727 silc_mutex_free(conn->internal->lock);
728 if (conn->internal->hash)
729 silc_hash_free(conn->internal->hash);
730 if (conn->internal->sha1hash)
731 silc_hash_free(conn->internal->sha1hash);
732 silc_atomic_uninit16(&conn->internal->cmd_ident);
733 silc_free(conn->internal->away_message);
734 if (conn->internal->rekey)
735 silc_ske_free_rekey_material(conn->internal->rekey);
736 if (conn->internal->cop)
737 silc_async_free(conn->internal->cop);
739 silc_free(conn->internal);
740 memset(conn, 'F', sizeof(*conn));
744 /******************************* Client API *********************************/
746 /* Connects to remote server. This is the main routine used to connect
747 to remote SILC server. Performs key exchange also. Returns the
748 connection context to the connection callback. */
751 silc_client_connect_to_server(SilcClient client,
752 SilcClientConnectionParams *params,
753 SilcPublicKey public_key,
754 SilcPrivateKey private_key,
755 char *remote_host, int port,
756 SilcClientConnectCallback callback,
759 SilcClientConnection conn;
761 SILC_LOG_DEBUG(("Connecting to server"));
763 if (!client || !remote_host)
766 /* Add new connection */
767 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
768 public_key, private_key, remote_host,
769 port, callback, context);
771 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
775 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
776 "Connecting to port %d of server %s",
779 /* Signal connection machine to start connecting */
780 conn->internal->connect = TRUE;
781 return conn->internal->cop;
784 /* Connects to remote client. Performs key exchange also. Returns the
785 connection context to the connection callback. */
788 silc_client_connect_to_client(SilcClient client,
789 SilcClientConnectionParams *params,
790 SilcPublicKey public_key,
791 SilcPrivateKey private_key,
792 char *remote_host, int port,
793 SilcClientConnectCallback callback,
796 SilcClientConnection conn;
798 SILC_LOG_DEBUG(("Connecting to client"));
800 if (!client || !remote_host)
804 params->no_authentication = TRUE;
806 /* Add new connection */
807 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
808 public_key, private_key, remote_host,
809 port, callback, context);
811 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
815 /* Signal connection machine to start connecting */
816 conn->internal->connect = TRUE;
817 return conn->internal->cop;
820 /* Starts key exchange in the remote stream indicated by `stream'. This
821 creates the connection context and returns it in the connection callback. */
824 silc_client_key_exchange(SilcClient client,
825 SilcClientConnectionParams *params,
826 SilcPublicKey public_key,
827 SilcPrivateKey private_key,
829 SilcConnectionType conn_type,
830 SilcClientConnectCallback callback,
833 SilcClientConnection conn;
837 SILC_LOG_DEBUG(("Performing key exchange"));
839 if (!client || !stream)
842 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
843 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
844 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
848 /* Add new connection */
849 conn = silc_client_add_connection(client, conn_type, TRUE, params,
850 public_key, private_key,
851 (char *)host, port, callback, context);
853 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
856 conn->stream = (void *)stream;
858 /* Signal connection to start key exchange */
859 conn->internal->key_exchange = TRUE;
860 return conn->internal->cop;
863 /* Closes remote connection */
865 void silc_client_close_connection(SilcClient client,
866 SilcClientConnection conn)
868 SILC_LOG_DEBUG(("Closing connection %p", conn));
870 /* Signal to close connection */
871 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
872 if (!conn->internal->disconnected) {
873 conn->internal->disconnected = TRUE;
874 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
878 /* Allocates new client object. This has to be done before client may
879 work. After calling this one must call silc_client_init to initialize
880 the client. The `application' is application specific user data pointer
881 and caller must free it. */
883 SilcClient silc_client_alloc(SilcClientOperations *ops,
884 SilcClientParams *params,
886 const char *version_string)
888 SilcClient new_client;
890 new_client = silc_calloc(1, sizeof(*new_client));
893 new_client->application = application;
895 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
896 if (!new_client->internal) {
897 silc_free(new_client);
900 new_client->internal->ops = ops;
901 new_client->internal->params =
902 silc_calloc(1, sizeof(*new_client->internal->params));
904 version_string = silc_version_string;
905 new_client->internal->silc_client_version = strdup(version_string);
908 memcpy(new_client->internal->params, params, sizeof(*params));
910 new_client->internal->params->
911 nickname_format[sizeof(new_client->internal->
912 params->nickname_format) - 1] = 0;
914 silc_atomic_init16(&new_client->internal->conns, 0);
919 /* Frees client object and its internals. */
921 void silc_client_free(SilcClient client)
923 silc_schedule_uninit(client->schedule);
926 silc_rng_free(client->rng);
928 if (!client->internal->params->dont_register_crypto_library) {
929 silc_cipher_unregister_all();
930 silc_pkcs_unregister_all();
931 silc_hash_unregister_all();
932 silc_hmac_unregister_all();
935 silc_packet_engine_stop(client->internal->packet_engine);
936 silc_dlist_uninit(client->internal->ftp_sessions);
937 silc_atomic_uninit16(&client->internal->conns);
938 silc_mutex_free(client->internal->lock);
939 silc_free(client->username);
940 silc_free(client->hostname);
941 silc_free(client->realname);
942 silc_free(client->internal->params);
943 silc_free(client->internal->silc_client_version);
944 silc_free(client->internal);
948 /* Initializes the client. This makes all the necessary steps to make
949 the client ready to be run. One must call silc_client_run to run the
950 client. Returns FALSE if error occured, TRUE otherwise. */
952 SilcBool silc_client_init(SilcClient client, const char *username,
953 const char *hostname, const char *realname,
954 SilcClientRunning running, void *context)
956 SILC_LOG_DEBUG(("Initializing client"));
961 if (!username || !hostname) {
962 SILC_LOG_ERROR(("Username and hostname must be given to "
963 "silc_client_init"));
969 /* Validate essential strings */
970 if (!silc_identifier_verify(username, strlen(username),
971 SILC_STRING_UTF8, 128)) {
972 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
976 if (!silc_identifier_verify(hostname, strlen(hostname),
977 SILC_STRING_UTF8, 256)) {
978 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
982 if (!silc_utf8_valid(realname, strlen(realname))) {
983 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
988 /* Take the name strings */
989 client->username = strdup(username);
990 client->hostname = strdup(hostname);
991 client->realname = strdup(realname);
992 if (!username || !hostname || !realname)
995 client->internal->ftp_sessions = silc_dlist_init();
996 if (!client->internal->ftp_sessions)
999 if (!client->internal->params->dont_register_crypto_library) {
1000 /* Initialize the crypto library. If application has done this already
1001 this has no effect. Also, we will not be overriding something
1002 application might have registered earlier. */
1003 silc_cipher_register_default();
1004 silc_pkcs_register_default();
1005 silc_hash_register_default();
1006 silc_hmac_register_default();
1009 /* Initialize random number generator */
1010 client->rng = silc_rng_alloc();
1013 silc_rng_init(client->rng);
1014 silc_rng_global_init(client->rng);
1016 /* Initialize the scheduler */
1017 client->schedule = silc_schedule_init(0, client);
1018 if (!client->schedule)
1021 /* Allocate client lock */
1022 silc_mutex_alloc(&client->internal->lock);
1024 /* Register commands */
1025 silc_client_commands_register(client);
1027 /* Start packet engine */
1028 client->internal->packet_engine =
1029 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1031 if (!client->internal->packet_engine)
1034 /* Initialize and start the client FSM */
1035 client->internal->running = running;
1036 client->internal->running_context = context;
1037 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1038 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1039 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1041 /* Signal the application when we are running */
1042 client->internal->run_callback = TRUE;
1043 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1048 /* Starts the SILC client FSM machine and blocks here. When this returns
1049 the client has ended. */
1051 void silc_client_run(SilcClient client)
1053 SILC_LOG_DEBUG(("Starting SILC client"));
1055 /* Run the scheduler */
1056 silc_schedule(client->schedule);
1059 /* Call scheduler one iteration and return. */
1061 void silc_client_run_one(SilcClient client)
1063 if (silc_fsm_is_started(&client->internal->fsm))
1064 silc_schedule_one(client->schedule, 0);
1067 /* Stops the client. This is called to stop the client and thus to stop
1070 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1073 SILC_LOG_DEBUG(("Stopping client"));
1075 client->internal->running = (SilcClientRunning)stopped;
1076 client->internal->running_context = context;
1078 /* Signal to stop */
1079 client->internal->stop = TRUE;
1080 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);