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 /************************ Static utility functions **************************/
27 /* Connection machine FSM destructor. This will finish the thread where
28 the machine was running and deletes the connection context. */
30 static void silc_client_connection_destructor(SilcFSM fsm,
32 void *destructor_context)
34 SilcClientConnection conn = fsm_context;
35 SilcFSMThread thread = destructor_context;
37 SILC_LOG_DEBUG(("Connection %p finished", conn));
39 /* Delete connection */
40 silc_client_del_connection(conn->client, conn);
42 /* Finish the thread were this machine was running. Its destructor is the
43 silc_client_connection_finished. */
44 silc_fsm_finish(thread);
47 /* Connection thread FSM destructor. This was the thread where the connection
48 machine was running (may be real thread). From here we notify client
49 that the connection thread has finished. */
51 static void silc_client_connection_finished(SilcFSMThread fsm,
53 void *destructor_context)
55 SilcClient client = silc_fsm_get_state_context(fsm);
57 /* Signal client that we have finished */
58 silc_atomic_sub_int16(&client->internal->conns, 1);
59 client->internal->connection_closed = TRUE;
60 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
65 /* Packet FSM thread destructor */
67 static void silc_client_packet_destructor(SilcFSMThread thread,
69 void *destructor_context)
71 SilcClientConnection conn = thread_context;
73 /* Add thread back to thread pool */
74 silc_list_add(conn->internal->thread_pool, thread);
75 if (silc_list_count(conn->internal->thread_pool) == 1)
76 silc_list_start(conn->internal->thread_pool);
79 /* Packet engine callback to receive a packet */
81 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
82 SilcPacketStream stream,
84 void *callback_context,
87 SilcClientConnection conn = stream_context;
90 /* Packets we do not handle */
91 switch (packet->type) {
92 case SILC_PACKET_HEARTBEAT:
93 case SILC_PACKET_SUCCESS:
94 case SILC_PACKET_FAILURE:
95 case SILC_PACKET_REJECT:
96 case SILC_PACKET_KEY_EXCHANGE:
97 case SILC_PACKET_KEY_EXCHANGE_1:
98 case SILC_PACKET_KEY_EXCHANGE_2:
99 case SILC_PACKET_REKEY_DONE:
100 case SILC_PACKET_CONNECTION_AUTH:
105 /* Get packet processing thread */
106 thread = silc_list_get(conn->internal->thread_pool);
108 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
109 silc_client_packet_destructor, NULL, FALSE);
113 silc_list_del(conn->internal->thread_pool, thread);
114 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
115 silc_client_packet_destructor, NULL, FALSE);
118 /* Process packet in thread */
119 silc_fsm_set_state_context(thread, packet);
120 silc_fsm_start_sync(thread, silc_client_connection_st_packet);
125 /* Packet engine callback to indicate end of stream */
127 static void silc_client_packet_eos(SilcPacketEngine engine,
128 SilcPacketStream stream,
129 void *callback_context,
130 void *stream_context)
132 SilcClientConnection conn = stream_context;
134 SILC_LOG_DEBUG(("Remote disconnected connection"));
136 /* Signal to close connection */
137 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
138 if (!conn->internal->disconnected) {
139 conn->internal->disconnected = TRUE;
140 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
144 /* Packet engine callback to indicate error */
146 static void silc_client_packet_error(SilcPacketEngine engine,
147 SilcPacketStream stream,
148 SilcPacketError error,
149 void *callback_context,
150 void *stream_context)
152 SilcClient client = callback_context;
153 SilcClientConnection conn = stream_context;
155 /* Read and write errors are silent */
156 if (error == SILC_PACKET_ERR_READ || error == SILC_PACKET_ERR_WRITE)
159 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
160 (char *)silc_packet_error_string(error));
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 /* Connection callback will not be called after user aborted connecting */
188 conn->callback = NULL;
189 conn->internal->cop = NULL;
191 /* Signal to close connection */
192 if (!conn->internal->disconnected) {
193 conn->internal->disconnected = TRUE;
195 /* If user aborts before connection machine is even up yet, then don't
196 send signal yet. It will process this event when it comes up. */
197 if (silc_fsm_is_started(&conn->internal->fsm))
198 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
202 /************************** Connection's machine ****************************/
204 /* Start the connection's state machine. If threads are in use the machine
205 is always executed in a real thread. */
207 SILC_FSM_STATE(silc_client_connection_st_start)
209 SilcClientConnection conn = fsm_context;
212 /* Take scheduler for connection */
213 conn->internal->schedule = silc_fsm_get_schedule(fsm);
215 /*** Run connection machine */
216 connfsm = &conn->internal->fsm;
217 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
218 fsm, conn->internal->schedule);
219 silc_fsm_event_init(&conn->internal->wait_event, connfsm);
220 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
222 /* Schedule any events possibly set in initialization */
223 if (conn->internal->disconnected)
224 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
225 if (conn->internal->connect)
226 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
227 if (conn->internal->key_exchange)
228 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
230 /* Wait until this thread is terminated from the machine destructor */
231 return SILC_FSM_WAIT;
234 /* Connection machine main state. This handles various connection related
235 events, but not packet processing. It's done in dedicated packet
236 processing FSM thread. */
238 SILC_FSM_STATE(silc_client_connection_st_run)
240 SilcClientConnection conn = fsm_context;
241 SilcFSMThread thread;
243 /* Wait for events */
244 SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
247 thread = &conn->internal->event_thread;
249 if (conn->internal->disconnected) {
250 /** Event: disconnected */
251 SILC_LOG_DEBUG(("Event: disconnected"));
252 silc_fsm_next(fsm, silc_client_connection_st_close);
253 return SILC_FSM_YIELD;
256 if (conn->internal->connect) {
257 SILC_LOG_DEBUG(("Event: connect"));
258 conn->internal->connect = FALSE;
259 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
261 /*** Event: connect */
262 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
264 silc_fsm_start_sync(thread, silc_client_st_connect);
265 return SILC_FSM_CONTINUE;
268 if (conn->internal->key_exchange) {
269 SILC_LOG_DEBUG(("Event: key exchange"));
270 conn->internal->key_exchange = FALSE;
271 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
273 /*** Event: key exchange */
274 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
276 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
277 return SILC_FSM_CONTINUE;
280 if (conn->internal->rekeying) {
281 SILC_LOG_DEBUG(("Event: rekey"));
282 conn->internal->rekeying = FALSE;
283 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
286 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
288 silc_fsm_start_sync(thread, silc_client_st_rekey);
289 return SILC_FSM_CONTINUE;
294 return SILC_FSM_CONTINUE;
297 /* Packet processor thread. Each incoming packet is processed in FSM
298 thread in this state. The thread is run in the connection machine. */
300 SILC_FSM_STATE(silc_client_connection_st_packet)
302 SilcClientConnection conn = fsm_context;
303 SilcPacket packet = state_context;
305 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
307 switch (packet->type) {
309 case SILC_PACKET_PRIVATE_MESSAGE:
310 /** Private message */
311 silc_fsm_next(fsm, silc_client_private_message);
314 case SILC_PACKET_CHANNEL_MESSAGE:
315 /** Channel message */
316 silc_fsm_next(fsm, silc_client_channel_message);
319 case SILC_PACKET_FTP:
320 /* File transfer packet */
321 silc_fsm_next(fsm, silc_client_ftp);
324 case SILC_PACKET_CHANNEL_KEY:
326 silc_fsm_next(fsm, silc_client_channel_key);
329 case SILC_PACKET_COMMAND_REPLY:
331 silc_fsm_next(fsm, silc_client_command_reply);
334 case SILC_PACKET_NOTIFY:
336 silc_fsm_next(fsm, silc_client_notify);
339 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
340 /* Private message key indicator */
341 silc_fsm_next(fsm, silc_client_private_message_key);
344 case SILC_PACKET_DISCONNECT:
346 silc_fsm_next(fsm, silc_client_disconnect);
349 case SILC_PACKET_ERROR:
350 /* Error by server */
351 silc_fsm_next(fsm, silc_client_error);
354 case SILC_PACKET_KEY_AGREEMENT:
356 silc_fsm_next(fsm, silc_client_key_agreement);
359 case SILC_PACKET_COMMAND:
360 /** Command packet */
361 silc_fsm_next(fsm, silc_client_command);
364 case SILC_PACKET_NEW_ID:
366 silc_fsm_next(fsm, silc_client_new_id);
369 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
370 /** Connection auth resolve reply */
371 silc_fsm_next(fsm, silc_client_connect_auth_request);
374 case SILC_PACKET_REKEY:
375 /* Signal to start rekey */
376 conn->internal->rekey_responder = TRUE;
377 conn->internal->rekeying = TRUE;
378 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
380 silc_packet_free(packet);
381 return SILC_FSM_FINISH;
385 silc_packet_free(packet);
386 return SILC_FSM_FINISH;
390 return SILC_FSM_CONTINUE;
393 /* Disconnection event to close remote connection. We close the connection
394 and finish the connection machine in this state. The connection context
395 is deleted in the machine destructor. The connection callback is called
396 in this state if it is set. */
398 SILC_FSM_STATE(silc_client_connection_st_close)
400 SilcClientConnection conn = fsm_context;
401 SilcClientCommandContext cmd;
403 /* Finish running command threads. This will also finish waiting packet
404 thread, as they are always waiting for some command. If any thread is
405 waiting something else than command, they must be finished explicitly. */
406 if (silc_list_count(conn->internal->pending_commands)) {
407 SILC_LOG_DEBUG(("Finish pending commands"));
408 silc_list_start(conn->internal->pending_commands);
409 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
410 if (silc_fsm_is_started(&cmd->thread)) {
411 cmd->verbose = FALSE;
412 silc_fsm_continue_sync(&cmd->thread);
416 /* Give threads time to finish */
417 return SILC_FSM_YIELD;
420 /* Abort ongoing event */
421 if (conn->internal->op) {
422 SILC_LOG_DEBUG(("Abort event"));
423 silc_async_abort(conn->internal->op, NULL, NULL);
424 conn->internal->op = NULL;
427 /* If event thread is running, finish it. */
428 if (silc_fsm_is_started(&conn->internal->event_thread)) {
429 SILC_LOG_DEBUG(("Finish event thread"));
430 silc_fsm_continue_sync(&conn->internal->event_thread);
431 return SILC_FSM_YIELD;
434 /* Call the connection callback */
436 conn->callback(conn->client, conn, conn->internal->status,
437 conn->internal->error, conn->internal->disconnect_message,
438 conn->callback_context);
439 silc_free(conn->internal->disconnect_message);
441 SILC_LOG_DEBUG(("Closing remote connection"));
443 /* Close connection. */
445 silc_packet_stream_destroy(conn->stream);
447 SILC_LOG_DEBUG(("Finishing connection machine"));
448 return SILC_FSM_FINISH;
451 /* Received error packet from server. Send it to application. */
453 SILC_FSM_STATE(silc_client_error)
455 SilcClientConnection conn = fsm_context;
456 SilcClient client = conn->client;
457 SilcPacket packet = state_context;
460 msg = silc_memdup(silc_buffer_data(&packet->buffer),
461 silc_buffer_len(&packet->buffer));
463 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
466 silc_packet_free(packet);
468 return SILC_FSM_FINISH;
471 /* Received disconnect packet from server. We close the connection and
472 send the disconnect message to application. */
474 SILC_FSM_STATE(silc_client_disconnect)
476 SilcClientConnection conn = fsm_context;
477 SilcPacket packet = state_context;
479 char *message = NULL;
481 SILC_LOG_DEBUG(("Server disconnected"));
483 if (silc_buffer_len(&packet->buffer) < 1) {
484 silc_packet_free(packet);
485 return SILC_FSM_FINISH;
488 status = (SilcStatus)packet->buffer.data[0];
490 silc_buffer_pull(&packet->buffer, 1);
491 if (silc_buffer_len(&packet->buffer) > 1 &&
492 silc_utf8_valid(silc_buffer_data(&packet->buffer),
493 silc_buffer_len(&packet->buffer)))
494 message = silc_memdup(silc_buffer_data(&packet->buffer),
495 silc_buffer_len(&packet->buffer));
497 /* Call connection callback */
498 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
499 conn->internal->error = status;
500 conn->internal->disconnect_message = message;
502 /* Signal to close connection */
503 if (!conn->internal->disconnected) {
504 conn->internal->disconnected = TRUE;
505 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
508 silc_packet_free(packet);
510 return SILC_FSM_FINISH;
513 /*************************** Main client machine ****************************/
515 /* The client's main state where we wait for various events */
517 SILC_FSM_STATE(silc_client_st_run)
519 SilcClient client = fsm_context;
521 /* Wait for events */
522 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
526 if (client->internal->run_callback) {
527 /* Call running callbcak back to application */
528 client->internal->run_callback = FALSE;
529 if (client->internal->running) {
530 SILC_LOG_DEBUG(("We are up, call running callback"));
531 client->internal->running(client, client->internal->running_context);
533 return SILC_FSM_CONTINUE;
536 if (client->internal->connection_closed) {
537 /* A connection finished */
538 SILC_LOG_DEBUG(("Event: connection closed"));
539 client->internal->connection_closed = FALSE;
540 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
541 client->internal->stop)
542 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
543 return SILC_FSM_CONTINUE;
546 if (client->internal->stop) {
547 /* Stop client libarry. If we have running connections, wait until
548 they finish first. */
549 if (silc_atomic_get_int16(&client->internal->conns) == 0) {
550 SILC_LOG_DEBUG(("Event: stop"));
551 silc_fsm_next(fsm, silc_client_st_stop);
553 return SILC_FSM_CONTINUE;
558 return SILC_FSM_CONTINUE;
561 /* Stop event. Stops the client library. */
563 SILC_FSM_STATE(silc_client_st_stop)
565 SilcClient client = fsm_context;
567 SILC_LOG_DEBUG(("Client stopped"));
570 silc_schedule_stop(client->schedule);
571 silc_client_commands_unregister(client);
573 /* Call stopped callback to application */
574 if (client->internal->running)
575 client->internal->running(client, client->internal->running_context);
577 return SILC_FSM_FINISH;
580 /******************************* Private API ********************************/
582 /* Adds new connection. Creates the connection context and returns it. */
585 silc_client_add_connection(SilcClient client,
586 SilcConnectionType conn_type,
588 SilcClientConnectionParams *params,
589 SilcPublicKey public_key,
590 SilcPrivateKey private_key,
591 char *remote_host, int port,
592 SilcClientConnectCallback callback,
595 SilcClientConnection conn;
596 SilcFSMThread thread;
601 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
603 conn = silc_calloc(1, sizeof(*conn));
607 conn->client = client;
608 conn->public_key = public_key;
609 conn->private_key = private_key;
610 conn->remote_host = strdup(remote_host);
611 conn->remote_port = port ? port : 706;
612 conn->type = conn_type;
613 conn->callback = callback;
614 conn->callback_context = context;
616 conn->internal = silc_calloc(1, sizeof(*conn->internal));
617 if (!conn->internal) {
621 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
622 silc_mutex_alloc(&conn->internal->lock);
623 silc_atomic_init16(&conn->internal->cmd_ident, 0);
625 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
627 silc_free(conn->internal);
633 conn->internal->params = *params;
634 if (!conn->internal->params.rekey_secs)
635 conn->internal->params.rekey_secs = 3600;
636 #ifndef SILC_DIST_INPLACE
637 if (conn->internal->params.rekey_secs < 300)
638 conn->internal->params.rekey_secs = 300;
639 #endif /* SILC_DIST_INPLACE */
641 conn->internal->verbose = TRUE;
642 silc_list_init(conn->internal->pending_commands,
643 struct SilcClientCommandContextStruct, next);
644 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
646 /* Allocate client, channel and serve caches */
647 if (conn_type != SILC_CONN_CLIENT) {
648 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
650 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
652 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
654 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
655 !conn->internal->server_cache) {
656 silc_client_del_connection(client, conn);
662 /* Initialize our async operation so that application may abort us
663 while we're connecting. */
664 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
666 if (!conn->internal->cop) {
667 silc_client_del_connection(client, conn);
672 /* Run the connection state machine. If threads are in use the connection
673 machine is always run in a real thread. */
674 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
675 silc_client_connection_finished, NULL,
676 client->internal->params->threads);
678 silc_client_del_connection(client, conn);
681 silc_fsm_set_state_context(thread, client);
682 silc_fsm_start(thread, silc_client_connection_st_start);
684 SILC_LOG_DEBUG(("New connection %p", conn));
685 silc_atomic_add_int16(&client->internal->conns, 1);
690 /* Deletes connection. This is always called from the connection machine
691 destructor. Do not call this directly other places. */
693 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
696 SilcIDCacheEntry entry;
697 SilcFSMThread thread;
699 SILC_LOG_DEBUG(("Freeing connection %p", conn));
701 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
703 /* Free all cache entries */
704 if (conn->internal->server_cache) {
705 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
706 silc_list_start(list);
707 while ((entry = silc_list_get(list)))
708 silc_client_del_server(client, conn, entry->context);
711 if (conn->internal->channel_cache) {
712 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
713 silc_list_start(list);
714 while ((entry = silc_list_get(list))) {
715 silc_client_empty_channel(client, conn, entry->context);
716 silc_client_del_channel(client, conn, entry->context);
720 if (conn->internal->client_cache) {
721 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
722 silc_list_start(list);
723 while ((entry = silc_list_get(list)))
724 silc_client_del_client(client, conn, entry->context);
729 if (conn->internal->client_cache)
730 silc_idcache_free(conn->internal->client_cache);
731 if (conn->internal->channel_cache)
732 silc_idcache_free(conn->internal->channel_cache);
733 if (conn->internal->server_cache)
734 silc_idcache_free(conn->internal->server_cache);
736 /* Free thread pool */
737 silc_list_start(conn->internal->thread_pool);
738 while ((thread = silc_list_get(conn->internal->thread_pool)))
739 silc_fsm_free(thread);
741 silc_free(conn->remote_host);
742 silc_buffer_free(conn->internal->local_idp);
743 silc_buffer_free(conn->internal->remote_idp);
744 silc_mutex_free(conn->internal->lock);
745 if (conn->internal->hash)
746 silc_hash_free(conn->internal->hash);
747 if (conn->internal->sha1hash)
748 silc_hash_free(conn->internal->sha1hash);
749 silc_atomic_uninit16(&conn->internal->cmd_ident);
750 silc_free(conn->internal->away_message);
751 if (conn->internal->rekey)
752 silc_ske_free_rekey_material(conn->internal->rekey);
753 if (conn->internal->cop)
754 silc_async_free(conn->internal->cop);
756 silc_free(conn->internal);
757 memset(conn, 'F', sizeof(*conn));
761 /******************************* Client API *********************************/
763 /* Connects to remote server. This is the main routine used to connect
764 to remote SILC server. Performs key exchange also. Returns the
765 connection context to the connection callback. */
768 silc_client_connect_to_server(SilcClient client,
769 SilcClientConnectionParams *params,
770 SilcPublicKey public_key,
771 SilcPrivateKey private_key,
772 char *remote_host, int port,
773 SilcClientConnectCallback callback,
776 SilcClientConnection conn;
778 SILC_LOG_DEBUG(("Connecting to server"));
780 if (!client || !remote_host)
783 if (client->internal->run_callback) {
784 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
785 "callback has not been called yet."));
789 /* Add new connection */
790 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
791 public_key, private_key, remote_host,
792 port, callback, context);
794 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
798 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
799 "Connecting to port %d of server %s",
802 /* Signal connection machine to start connecting */
803 conn->internal->connect = TRUE;
804 return conn->internal->cop;
807 /* Connects to remote client. Performs key exchange also. Returns the
808 connection context to the connection callback. */
811 silc_client_connect_to_client(SilcClient client,
812 SilcClientConnectionParams *params,
813 SilcPublicKey public_key,
814 SilcPrivateKey private_key,
815 char *remote_host, int port,
816 SilcClientConnectCallback callback,
819 SilcClientConnection conn;
821 SILC_LOG_DEBUG(("Connecting to client"));
823 if (!client || !remote_host)
826 if (client->internal->run_callback) {
827 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
828 "callback has not been called yet."));
833 params->no_authentication = TRUE;
835 /* Add new connection */
836 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
837 public_key, private_key, remote_host,
838 port, callback, context);
840 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
844 /* Signal connection machine to start connecting */
845 conn->internal->connect = TRUE;
846 return conn->internal->cop;
849 /* Starts key exchange in the remote stream indicated by `stream'. This
850 creates the connection context and returns it in the connection callback. */
853 silc_client_key_exchange(SilcClient client,
854 SilcClientConnectionParams *params,
855 SilcPublicKey public_key,
856 SilcPrivateKey private_key,
858 SilcConnectionType conn_type,
859 SilcClientConnectCallback callback,
862 SilcClientConnection conn;
866 SILC_LOG_DEBUG(("Performing key exchange"));
868 if (!client || !stream)
871 if (client->internal->run_callback) {
872 SILC_LOG_ERROR(("Client library is not started yet. SilcClientRunning "
873 "callback has not been called yet."));
877 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
878 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
879 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
883 /* Add new connection */
884 conn = silc_client_add_connection(client, conn_type, TRUE, params,
885 public_key, private_key,
886 (char *)host, port, callback, context);
888 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
891 conn->internal->user_stream = stream;
893 /* Signal connection to start key exchange */
894 conn->internal->key_exchange = TRUE;
895 return conn->internal->cop;
898 /* Closes remote connection */
900 void silc_client_close_connection(SilcClient client,
901 SilcClientConnection conn)
903 SILC_LOG_DEBUG(("Closing connection %p", conn));
905 /* Signal to close connection */
906 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
907 if (!conn->internal->disconnected) {
908 conn->internal->disconnected = TRUE;
909 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
913 /* Allocates new client object. This has to be done before client may
914 work. After calling this one must call silc_client_init to initialize
915 the client. The `application' is application specific user data pointer
916 and caller must free it. */
918 SilcClient silc_client_alloc(SilcClientOperations *ops,
919 SilcClientParams *params,
921 const char *version_string)
923 SilcClient new_client;
925 new_client = silc_calloc(1, sizeof(*new_client));
928 new_client->application = application;
930 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
931 if (!new_client->internal) {
932 silc_free(new_client);
935 new_client->internal->ops = ops;
936 new_client->internal->params =
937 silc_calloc(1, sizeof(*new_client->internal->params));
939 version_string = silc_version_string;
940 new_client->internal->silc_client_version = strdup(version_string);
943 memcpy(new_client->internal->params, params, sizeof(*params));
945 new_client->internal->params->
946 nickname_format[sizeof(new_client->internal->
947 params->nickname_format) - 1] = 0;
949 silc_atomic_init16(&new_client->internal->conns, 0);
954 /* Frees client object and its internals. */
956 void silc_client_free(SilcClient client)
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 silc_packet_engine_stop(client->internal->packet_engine);
967 silc_dlist_uninit(client->internal->ftp_sessions);
968 silc_atomic_uninit16(&client->internal->conns);
969 silc_mutex_free(client->internal->lock);
970 silc_free(client->username);
971 silc_free(client->hostname);
972 silc_free(client->realname);
973 silc_free(client->internal->params);
974 silc_free(client->internal->silc_client_version);
975 silc_free(client->internal);
979 /* Initializes the client. This makes all the necessary steps to make
980 the client ready to be run. One must call silc_client_run to run the
981 client. Returns FALSE if error occured, TRUE otherwise. */
983 SilcBool silc_client_init(SilcClient client, const char *username,
984 const char *hostname, const char *realname,
985 SilcClientRunning running, void *context)
987 SILC_LOG_DEBUG(("Initializing client"));
992 if (!username || !hostname) {
993 SILC_LOG_ERROR(("Username and hostname must be given to "
994 "silc_client_init"));
1000 /* Validate essential strings */
1001 if (!silc_identifier_verify(username, strlen(username),
1002 SILC_STRING_UTF8, 128)) {
1003 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1007 if (!silc_identifier_verify(hostname, strlen(hostname),
1008 SILC_STRING_UTF8, 256)) {
1009 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1013 if (!silc_utf8_valid(realname, strlen(realname))) {
1014 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1019 /* Take the name strings */
1020 client->username = strdup(username);
1021 client->hostname = strdup(hostname);
1022 client->realname = strdup(realname);
1023 if (!username || !hostname || !realname)
1026 client->internal->ftp_sessions = silc_dlist_init();
1027 if (!client->internal->ftp_sessions)
1030 if (!client->internal->params->dont_register_crypto_library)
1031 /* Initialize the crypto library. If application has done this already
1032 this has no effect. */
1033 silc_crypto_init(NULL);
1035 /* Initialize random number generator */
1036 client->rng = silc_rng_alloc();
1039 silc_rng_init(client->rng);
1040 silc_rng_global_init(client->rng);
1042 /* Initialize the scheduler */
1043 client->schedule = silc_schedule_init(0, client, NULL);
1044 if (!client->schedule)
1047 /* Allocate client lock */
1048 silc_mutex_alloc(&client->internal->lock);
1050 /* Register commands */
1051 silc_client_commands_register(client);
1053 /* Start packet engine */
1054 client->internal->packet_engine =
1055 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1057 if (!client->internal->packet_engine)
1060 /* Initialize and start the client FSM */
1061 client->internal->running = running;
1062 client->internal->running_context = context;
1063 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1064 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1065 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1067 /* Signal the application when we are running */
1068 client->internal->run_callback = TRUE;
1069 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1074 /* Starts the SILC client FSM machine and blocks here. When this returns
1075 the client has ended. */
1077 void silc_client_run(SilcClient client)
1079 SILC_LOG_DEBUG(("Starting SILC client"));
1081 /* Run the scheduler */
1082 silc_schedule(client->schedule);
1085 /* Call scheduler one iteration and return. */
1087 void silc_client_run_one(SilcClient client)
1089 if (silc_fsm_is_started(&client->internal->fsm))
1090 silc_schedule_one(client->schedule, 0);
1093 /* Stops the client. This is called to stop the client and thus to stop
1096 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1099 SILC_LOG_DEBUG(("Stopping client"));
1101 if (!silc_fsm_is_started(&client->internal->fsm)) {
1102 SILC_LOG_WARNING(("Attempting to stop client library before it has been "
1103 "started (silc_client_init not called)"));
1107 client->internal->running = (SilcClientRunning)stopped;
1108 client->internal->running_context = context;
1110 /* Signal to stop */
1111 client->internal->stop = TRUE;
1112 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);