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;
183 conn->internal->cop = NULL;
185 /* Signal to close connection */
186 if (!conn->internal->disconnected) {
187 conn->internal->disconnected = TRUE;
189 /* If user aborts before connection machine is even up yet, then don't
190 send signal yet. It will process this event when it comes up. */
191 if (silc_fsm_is_started(&conn->internal->fsm))
192 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
196 /************************** Connection's machine ****************************/
198 /* Start the connection's state machine. If threads are in use the machine
199 is always executed in a real thread. */
201 SILC_FSM_STATE(silc_client_connection_st_start)
203 SilcClientConnection conn = fsm_context;
206 /* Take scheduler for connection */
207 conn->internal->schedule = silc_fsm_get_schedule(fsm);
209 /*** Run connection machine */
210 connfsm = &conn->internal->fsm;
211 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
212 fsm, conn->internal->schedule);
213 silc_fsm_event_init(&conn->internal->wait_event, connfsm);
214 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
216 /* Schedule any events possibly set in initialization */
217 if (conn->internal->disconnected)
218 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
219 if (conn->internal->connect)
220 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
221 if (conn->internal->key_exchange)
222 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
224 /* Wait until this thread is terminated from the machine destructor */
225 return SILC_FSM_WAIT;
228 /* Connection machine main state. This handles various connection related
229 events, but not packet processing. It's done in dedicated packet
230 processing FSM thread. */
232 SILC_FSM_STATE(silc_client_connection_st_run)
234 SilcClientConnection conn = fsm_context;
235 SilcFSMThread thread;
237 /* Wait for events */
238 SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
241 thread = &conn->internal->event_thread;
243 if (conn->internal->disconnected) {
244 /** Event: disconnected */
245 SILC_LOG_DEBUG(("Event: disconnected"));
246 silc_fsm_next(fsm, silc_client_connection_st_close);
247 return SILC_FSM_YIELD;
250 if (conn->internal->connect) {
251 SILC_LOG_DEBUG(("Event: connect"));
252 conn->internal->connect = FALSE;
253 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
255 /*** Event: connect */
256 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
258 silc_fsm_start_sync(thread, silc_client_st_connect);
259 return SILC_FSM_CONTINUE;
262 if (conn->internal->key_exchange) {
263 SILC_LOG_DEBUG(("Event: key exchange"));
264 conn->internal->key_exchange = FALSE;
265 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
267 /*** Event: key exchange */
268 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
270 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
271 return SILC_FSM_CONTINUE;
274 if (conn->internal->rekeying) {
275 SILC_LOG_DEBUG(("Event: rekey"));
276 conn->internal->rekeying = FALSE;
277 SILC_ASSERT(silc_fsm_is_started(thread) == FALSE);
280 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
282 silc_fsm_start_sync(thread, silc_client_st_rekey);
283 return SILC_FSM_CONTINUE;
288 return SILC_FSM_CONTINUE;
291 /* Packet processor thread. Each incoming packet is processed in FSM
292 thread in this state. The thread is run in the connection machine. */
294 SILC_FSM_STATE(silc_client_connection_st_packet)
296 SilcClientConnection conn = fsm_context;
297 SilcPacket packet = state_context;
299 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
301 switch (packet->type) {
303 case SILC_PACKET_PRIVATE_MESSAGE:
304 /** Private message */
305 silc_fsm_next(fsm, silc_client_private_message);
308 case SILC_PACKET_CHANNEL_MESSAGE:
309 /** Channel message */
310 silc_fsm_next(fsm, silc_client_channel_message);
313 case SILC_PACKET_FTP:
314 /* File transfer packet */
315 silc_fsm_next(fsm, silc_client_ftp);
318 case SILC_PACKET_CHANNEL_KEY:
320 silc_fsm_next(fsm, silc_client_channel_key);
323 case SILC_PACKET_COMMAND_REPLY:
325 silc_fsm_next(fsm, silc_client_command_reply);
328 case SILC_PACKET_NOTIFY:
330 silc_fsm_next(fsm, silc_client_notify);
333 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
334 /* Private message key indicator */
335 silc_fsm_next(fsm, silc_client_private_message_key);
338 case SILC_PACKET_DISCONNECT:
340 silc_fsm_next(fsm, silc_client_disconnect);
343 case SILC_PACKET_ERROR:
344 /* Error by server */
345 silc_fsm_next(fsm, silc_client_error);
348 case SILC_PACKET_KEY_AGREEMENT:
350 silc_fsm_next(fsm, silc_client_key_agreement);
353 case SILC_PACKET_COMMAND:
354 /** Command packet */
355 silc_fsm_next(fsm, silc_client_command);
358 case SILC_PACKET_NEW_ID:
360 silc_fsm_next(fsm, silc_client_new_id);
363 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
364 /** Connection auth resolve reply */
365 silc_fsm_next(fsm, silc_client_connect_auth_request);
368 case SILC_PACKET_REKEY:
369 /* Signal to start rekey */
370 conn->internal->rekey_responder = TRUE;
371 conn->internal->rekeying = TRUE;
372 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
374 silc_packet_free(packet);
375 return SILC_FSM_FINISH;
379 silc_packet_free(packet);
380 return SILC_FSM_FINISH;
384 return SILC_FSM_CONTINUE;
387 /* Disconnection event to close remote connection. We close the connection
388 and finish the connection machine in this state. The connection context
389 is deleted in the machine destructor. The connection callback is called
390 in this state if it is set. */
392 SILC_FSM_STATE(silc_client_connection_st_close)
394 SilcClientConnection conn = fsm_context;
395 SilcClientCommandContext cmd;
397 /* Finish running command threads. This will also finish waiting packet
398 thread, as they are always waiting for some command. If any thread is
399 waiting something else than command, they must be finished explicitly. */
400 if (silc_list_count(conn->internal->pending_commands)) {
401 SILC_LOG_DEBUG(("Finish pending commands"));
402 silc_list_start(conn->internal->pending_commands);
403 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
404 if (silc_fsm_is_started(&cmd->thread)) {
405 cmd->verbose = FALSE;
406 silc_fsm_continue_sync(&cmd->thread);
410 /* Give threads time to finish */
411 return SILC_FSM_YIELD;
414 /* Abort ongoing event */
415 if (conn->internal->op) {
416 SILC_LOG_DEBUG(("Abort event"));
417 silc_async_abort(conn->internal->op, NULL, NULL);
418 conn->internal->op = NULL;
421 /* If event thread is running, finish it. */
422 if (silc_fsm_is_started(&conn->internal->event_thread)) {
423 SILC_LOG_DEBUG(("Finish event thread"));
424 silc_fsm_continue_sync(&conn->internal->event_thread);
425 return SILC_FSM_YIELD;
428 /* Call the connection callback */
430 conn->callback(conn->client, conn, conn->internal->status,
431 conn->internal->error, conn->internal->disconnect_message,
432 conn->callback_context);
433 silc_free(conn->internal->disconnect_message);
435 SILC_LOG_DEBUG(("Closing remote connection"));
437 /* Close connection. */
439 silc_packet_stream_destroy(conn->stream);
441 SILC_LOG_DEBUG(("Finishing connection machine"));
442 return SILC_FSM_FINISH;
445 /* Received error packet from server. Send it to application. */
447 SILC_FSM_STATE(silc_client_error)
449 SilcClientConnection conn = fsm_context;
450 SilcClient client = conn->client;
451 SilcPacket packet = state_context;
454 msg = silc_memdup(silc_buffer_data(&packet->buffer),
455 silc_buffer_len(&packet->buffer));
457 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
460 silc_packet_free(packet);
462 return SILC_FSM_FINISH;
465 /* Received disconnect packet from server. We close the connection and
466 send the disconnect message to application. */
468 SILC_FSM_STATE(silc_client_disconnect)
470 SilcClientConnection conn = fsm_context;
471 SilcPacket packet = state_context;
473 char *message = NULL;
475 SILC_LOG_DEBUG(("Server disconnected"));
477 if (silc_buffer_len(&packet->buffer) < 1) {
478 silc_packet_free(packet);
479 return SILC_FSM_FINISH;
482 status = (SilcStatus)packet->buffer.data[0];
484 silc_buffer_pull(&packet->buffer, 1);
485 if (silc_buffer_len(&packet->buffer) > 1 &&
486 silc_utf8_valid(silc_buffer_data(&packet->buffer),
487 silc_buffer_len(&packet->buffer)))
488 message = silc_memdup(silc_buffer_data(&packet->buffer),
489 silc_buffer_len(&packet->buffer));
491 /* Call connection callback */
492 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
493 conn->internal->error = status;
494 conn->internal->disconnect_message = message;
496 /* Signal to close connection */
497 if (!conn->internal->disconnected) {
498 conn->internal->disconnected = TRUE;
499 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
502 silc_packet_free(packet);
504 return SILC_FSM_FINISH;
507 /*************************** Main client machine ****************************/
509 /* The client's main state where we wait for various events */
511 SILC_FSM_STATE(silc_client_st_run)
513 SilcClient client = fsm_context;
515 /* Wait for events */
516 SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
520 if (client->internal->run_callback && client->internal->running) {
521 /* Call running callbcak back to application */
522 SILC_LOG_DEBUG(("We are up, call running callback"));
523 client->internal->run_callback = FALSE;
524 client->internal->running(client, client->internal->running_context);
525 return SILC_FSM_CONTINUE;
528 if (client->internal->connection_closed) {
529 /* A connection finished */
530 SILC_LOG_DEBUG(("Event: connection closed"));
531 client->internal->connection_closed = FALSE;
532 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
533 client->internal->stop)
534 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
535 return SILC_FSM_CONTINUE;
538 if (client->internal->stop) {
539 /* Stop client libarry. If we have running connections, wait until
540 they finish first. */
541 if (silc_atomic_get_int16(&client->internal->conns) == 0) {
542 SILC_LOG_DEBUG(("Event: stop"));
543 silc_fsm_next(fsm, silc_client_st_stop);
545 return SILC_FSM_CONTINUE;
550 return SILC_FSM_CONTINUE;
553 /* Stop event. Stops the client library. */
555 SILC_FSM_STATE(silc_client_st_stop)
557 SilcClient client = fsm_context;
559 SILC_LOG_DEBUG(("Client stopped"));
562 silc_schedule_stop(client->schedule);
563 silc_client_commands_unregister(client);
565 /* Call stopped callback to application */
566 if (client->internal->running)
567 client->internal->running(client, client->internal->running_context);
569 return SILC_FSM_FINISH;
572 /******************************* Private API ********************************/
574 /* Adds new connection. Creates the connection context and returns it. */
577 silc_client_add_connection(SilcClient client,
578 SilcConnectionType conn_type,
580 SilcClientConnectionParams *params,
581 SilcPublicKey public_key,
582 SilcPrivateKey private_key,
583 char *remote_host, int port,
584 SilcClientConnectCallback callback,
587 SilcClientConnection conn;
588 SilcFSMThread thread;
593 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
595 conn = silc_calloc(1, sizeof(*conn));
599 conn->client = client;
600 conn->public_key = public_key;
601 conn->private_key = private_key;
602 conn->remote_host = strdup(remote_host);
603 conn->remote_port = port ? port : 706;
604 conn->type = conn_type;
605 conn->callback = callback;
606 conn->callback_context = context;
608 conn->internal = silc_calloc(1, sizeof(*conn->internal));
609 if (!conn->internal) {
613 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
614 silc_mutex_alloc(&conn->internal->lock);
615 silc_atomic_init16(&conn->internal->cmd_ident, 0);
617 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
619 silc_free(conn->internal);
625 conn->internal->params = *params;
626 if (!conn->internal->params.rekey_secs)
627 conn->internal->params.rekey_secs = 3600;
628 #ifndef SILC_DIST_INPLACE
629 if (conn->internal->params.rekey_secs < 300)
630 conn->internal->params.rekey_secs = 300;
631 #endif /* SILC_DIST_INPLACE */
633 conn->internal->verbose = TRUE;
634 silc_list_init(conn->internal->pending_commands,
635 struct SilcClientCommandContextStruct, next);
636 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
638 /* Allocate client, channel and serve caches */
639 if (conn_type != SILC_CONN_CLIENT) {
640 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
642 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
644 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
646 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
647 !conn->internal->server_cache) {
648 silc_client_del_connection(client, conn);
654 /* Initialize our async operation so that application may abort us
655 while we're connecting. */
656 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
658 if (!conn->internal->cop) {
659 silc_client_del_connection(client, conn);
664 /* Run the connection state machine. If threads are in use the connection
665 machine is always run in a real thread. */
666 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
667 silc_client_connection_finished, NULL,
668 client->internal->params->threads);
670 silc_client_del_connection(client, conn);
673 silc_fsm_set_state_context(thread, client);
674 silc_fsm_start(thread, silc_client_connection_st_start);
676 SILC_LOG_DEBUG(("New connection %p", conn));
677 silc_atomic_add_int16(&client->internal->conns, 1);
682 /* Deletes connection. This is always called from the connection machine
683 destructor. Do not call this directly other places. */
685 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
688 SilcIDCacheEntry entry;
689 SilcFSMThread thread;
691 SILC_LOG_DEBUG(("Freeing connection %p", conn));
693 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
695 /* Free all cache entries */
696 if (conn->internal->server_cache) {
697 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
698 silc_list_start(list);
699 while ((entry = silc_list_get(list)))
700 silc_client_del_server(client, conn, entry->context);
703 if (conn->internal->channel_cache) {
704 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
705 silc_list_start(list);
706 while ((entry = silc_list_get(list))) {
707 silc_client_empty_channel(client, conn, entry->context);
708 silc_client_del_channel(client, conn, entry->context);
712 if (conn->internal->client_cache) {
713 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
714 silc_list_start(list);
715 while ((entry = silc_list_get(list)))
716 silc_client_del_client(client, conn, entry->context);
721 if (conn->internal->client_cache)
722 silc_idcache_free(conn->internal->client_cache);
723 if (conn->internal->channel_cache)
724 silc_idcache_free(conn->internal->channel_cache);
725 if (conn->internal->server_cache)
726 silc_idcache_free(conn->internal->server_cache);
728 /* Free thread pool */
729 silc_list_start(conn->internal->thread_pool);
730 while ((thread = silc_list_get(conn->internal->thread_pool)))
731 silc_fsm_free(thread);
733 silc_free(conn->remote_host);
734 silc_buffer_free(conn->internal->local_idp);
735 silc_buffer_free(conn->internal->remote_idp);
736 silc_mutex_free(conn->internal->lock);
737 if (conn->internal->hash)
738 silc_hash_free(conn->internal->hash);
739 if (conn->internal->sha1hash)
740 silc_hash_free(conn->internal->sha1hash);
741 silc_atomic_uninit16(&conn->internal->cmd_ident);
742 silc_free(conn->internal->away_message);
743 if (conn->internal->rekey)
744 silc_ske_free_rekey_material(conn->internal->rekey);
745 if (conn->internal->cop)
746 silc_async_free(conn->internal->cop);
748 silc_free(conn->internal);
749 memset(conn, 'F', sizeof(*conn));
753 /******************************* Client API *********************************/
755 /* Connects to remote server. This is the main routine used to connect
756 to remote SILC server. Performs key exchange also. Returns the
757 connection context to the connection callback. */
760 silc_client_connect_to_server(SilcClient client,
761 SilcClientConnectionParams *params,
762 SilcPublicKey public_key,
763 SilcPrivateKey private_key,
764 char *remote_host, int port,
765 SilcClientConnectCallback callback,
768 SilcClientConnection conn;
770 SILC_LOG_DEBUG(("Connecting to server"));
772 if (!client || !remote_host)
775 /* Add new connection */
776 conn = silc_client_add_connection(client, SILC_CONN_SERVER, TRUE, params,
777 public_key, private_key, remote_host,
778 port, callback, context);
780 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
784 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
785 "Connecting to port %d of server %s",
788 /* Signal connection machine to start connecting */
789 conn->internal->connect = TRUE;
790 return conn->internal->cop;
793 /* Connects to remote client. Performs key exchange also. Returns the
794 connection context to the connection callback. */
797 silc_client_connect_to_client(SilcClient client,
798 SilcClientConnectionParams *params,
799 SilcPublicKey public_key,
800 SilcPrivateKey private_key,
801 char *remote_host, int port,
802 SilcClientConnectCallback callback,
805 SilcClientConnection conn;
807 SILC_LOG_DEBUG(("Connecting to client"));
809 if (!client || !remote_host)
813 params->no_authentication = TRUE;
815 /* Add new connection */
816 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, TRUE, params,
817 public_key, private_key, remote_host,
818 port, callback, context);
820 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
824 /* Signal connection machine to start connecting */
825 conn->internal->connect = TRUE;
826 return conn->internal->cop;
829 /* Starts key exchange in the remote stream indicated by `stream'. This
830 creates the connection context and returns it in the connection callback. */
833 silc_client_key_exchange(SilcClient client,
834 SilcClientConnectionParams *params,
835 SilcPublicKey public_key,
836 SilcPrivateKey private_key,
838 SilcConnectionType conn_type,
839 SilcClientConnectCallback callback,
842 SilcClientConnection conn;
846 SILC_LOG_DEBUG(("Performing key exchange"));
848 if (!client || !stream)
851 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
852 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
853 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
857 /* Add new connection */
858 conn = silc_client_add_connection(client, conn_type, TRUE, params,
859 public_key, private_key,
860 (char *)host, port, callback, context);
862 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
865 conn->internal->user_stream = stream;
867 /* Signal connection to start key exchange */
868 conn->internal->key_exchange = TRUE;
869 return conn->internal->cop;
872 /* Closes remote connection */
874 void silc_client_close_connection(SilcClient client,
875 SilcClientConnection conn)
877 SILC_LOG_DEBUG(("Closing connection %p", conn));
879 /* Signal to close connection */
880 conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
881 if (!conn->internal->disconnected) {
882 conn->internal->disconnected = TRUE;
883 SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
887 /* Allocates new client object. This has to be done before client may
888 work. After calling this one must call silc_client_init to initialize
889 the client. The `application' is application specific user data pointer
890 and caller must free it. */
892 SilcClient silc_client_alloc(SilcClientOperations *ops,
893 SilcClientParams *params,
895 const char *version_string)
897 SilcClient new_client;
899 new_client = silc_calloc(1, sizeof(*new_client));
902 new_client->application = application;
904 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
905 if (!new_client->internal) {
906 silc_free(new_client);
909 new_client->internal->ops = ops;
910 new_client->internal->params =
911 silc_calloc(1, sizeof(*new_client->internal->params));
913 version_string = silc_version_string;
914 new_client->internal->silc_client_version = strdup(version_string);
917 memcpy(new_client->internal->params, params, sizeof(*params));
919 new_client->internal->params->
920 nickname_format[sizeof(new_client->internal->
921 params->nickname_format) - 1] = 0;
923 silc_atomic_init16(&new_client->internal->conns, 0);
928 /* Frees client object and its internals. */
930 void silc_client_free(SilcClient client)
932 silc_schedule_uninit(client->schedule);
935 silc_rng_free(client->rng);
937 if (!client->internal->params->dont_register_crypto_library) {
938 silc_cipher_unregister_all();
939 silc_pkcs_unregister_all();
940 silc_hash_unregister_all();
941 silc_hmac_unregister_all();
944 silc_packet_engine_stop(client->internal->packet_engine);
945 silc_dlist_uninit(client->internal->ftp_sessions);
946 silc_atomic_uninit16(&client->internal->conns);
947 silc_mutex_free(client->internal->lock);
948 silc_free(client->username);
949 silc_free(client->hostname);
950 silc_free(client->realname);
951 silc_free(client->internal->params);
952 silc_free(client->internal->silc_client_version);
953 silc_free(client->internal);
957 /* Initializes the client. This makes all the necessary steps to make
958 the client ready to be run. One must call silc_client_run to run the
959 client. Returns FALSE if error occured, TRUE otherwise. */
961 SilcBool silc_client_init(SilcClient client, const char *username,
962 const char *hostname, const char *realname,
963 SilcClientRunning running, void *context)
965 SILC_LOG_DEBUG(("Initializing client"));
970 if (!username || !hostname) {
971 SILC_LOG_ERROR(("Username and hostname must be given to "
972 "silc_client_init"));
978 /* Validate essential strings */
979 if (!silc_identifier_verify(username, strlen(username),
980 SILC_STRING_UTF8, 128)) {
981 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
985 if (!silc_identifier_verify(hostname, strlen(hostname),
986 SILC_STRING_UTF8, 256)) {
987 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
991 if (!silc_utf8_valid(realname, strlen(realname))) {
992 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
997 /* Take the name strings */
998 client->username = strdup(username);
999 client->hostname = strdup(hostname);
1000 client->realname = strdup(realname);
1001 if (!username || !hostname || !realname)
1004 client->internal->ftp_sessions = silc_dlist_init();
1005 if (!client->internal->ftp_sessions)
1008 if (!client->internal->params->dont_register_crypto_library) {
1009 /* Initialize the crypto library. If application has done this already
1010 this has no effect. Also, we will not be overriding something
1011 application might have registered earlier. */
1012 silc_cipher_register_default();
1013 silc_pkcs_register_default();
1014 silc_hash_register_default();
1015 silc_hmac_register_default();
1018 /* Initialize random number generator */
1019 client->rng = silc_rng_alloc();
1022 silc_rng_init(client->rng);
1023 silc_rng_global_init(client->rng);
1025 /* Initialize the scheduler */
1026 client->schedule = silc_schedule_init(0, client);
1027 if (!client->schedule)
1030 /* Allocate client lock */
1031 silc_mutex_alloc(&client->internal->lock);
1033 /* Register commands */
1034 silc_client_commands_register(client);
1036 /* Start packet engine */
1037 client->internal->packet_engine =
1038 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1040 if (!client->internal->packet_engine)
1043 /* Initialize and start the client FSM */
1044 client->internal->running = running;
1045 client->internal->running_context = context;
1046 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1047 silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1048 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1050 /* Signal the application when we are running */
1051 client->internal->run_callback = TRUE;
1052 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1057 /* Starts the SILC client FSM machine and blocks here. When this returns
1058 the client has ended. */
1060 void silc_client_run(SilcClient client)
1062 SILC_LOG_DEBUG(("Starting SILC client"));
1064 /* Run the scheduler */
1065 silc_schedule(client->schedule);
1068 /* Call scheduler one iteration and return. */
1070 void silc_client_run_one(SilcClient client)
1072 if (silc_fsm_is_started(&client->internal->fsm))
1073 silc_schedule_one(client->schedule, 0);
1076 /* Stops the client. This is called to stop the client and thus to stop
1079 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1082 SILC_LOG_DEBUG(("Stopping client"));
1084 client->internal->running = (SilcClientRunning)stopped;
1085 client->internal->running_context = context;
1087 /* Signal to stop */
1088 client->internal->stop = TRUE;
1089 SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);