5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2006 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_SEMA_POST(&client->internal->wait_event);
68 /* Packet FSM thread destructor */
70 static void silc_client_packet_destructor(SilcFSMThread thread,
72 void *destructor_context)
74 SilcClientConnection conn = thread_context;
76 /* Add thread back to thread pool */
77 silc_list_add(conn->internal->thread_pool, thread);
78 if (silc_list_count(conn->internal->thread_pool) == 1)
79 silc_list_start(conn->internal->thread_pool);
82 /* Packet engine callback to receive a packet */
84 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
85 SilcPacketStream stream,
87 void *callback_context,
90 SilcClientConnection conn = stream_context;
93 /* Packets we do not handle */
94 switch (packet->type) {
95 case SILC_PACKET_HEARTBEAT:
96 case SILC_PACKET_SUCCESS:
97 case SILC_PACKET_FAILURE:
98 case SILC_PACKET_REJECT:
99 case SILC_PACKET_KEY_EXCHANGE:
100 case SILC_PACKET_KEY_EXCHANGE_1:
101 case SILC_PACKET_KEY_EXCHANGE_2:
102 case SILC_PACKET_REKEY_DONE:
103 case SILC_PACKET_CONNECTION_AUTH:
104 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
109 /* Get packet processing thread */
110 thread = silc_list_get(conn->internal->thread_pool);
112 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
113 silc_client_packet_destructor, NULL, FALSE);
117 silc_list_del(conn->internal->thread_pool, thread);
118 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
119 silc_client_packet_destructor, NULL, FALSE);
122 /* Process packet in thread */
123 silc_fsm_set_state_context(thread, packet);
124 silc_fsm_start_sync(thread, silc_client_connection_st_packet);
129 /* Packet engine callback to indicate end of stream */
131 static void silc_client_packet_eos(SilcPacketEngine engine,
132 SilcPacketStream stream,
133 void *callback_context,
134 void *stream_context)
136 SilcClientConnection conn = stream_context;
137 SilcClient client = conn->client;
139 SILC_LOG_DEBUG(("Remote disconnected connection"));
141 /* Call connection callback */
142 if (!conn->internal->callback_called)
143 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, 0, NULL,
144 conn->callback_context);
145 conn->internal->callback_called = TRUE;
147 /* Signal to close connection */
148 if (!conn->internal->disconnected) {
149 conn->internal->disconnected = TRUE;
150 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
154 /* Packet engine callback to indicate error */
156 static void silc_client_packet_error(SilcPacketEngine engine,
157 SilcPacketStream stream,
158 SilcPacketError error,
159 void *callback_context,
160 void *stream_context)
165 /* Packet stream callbacks */
166 static SilcPacketCallbacks silc_client_stream_cbs =
168 silc_client_packet_receive,
169 silc_client_packet_eos,
170 silc_client_packet_error
175 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
176 void *destructor_context)
181 /* Connect abort operation */
183 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
185 SilcClientConnection conn = context;
187 SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
189 /* Signal to close connection */
190 if (!conn->internal->disconnected) {
191 conn->internal->disconnected = TRUE;
192 SILC_FSM_SEMA_POST(&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_sema_init(&conn->internal->wait_event, connfsm, 0);
214 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
216 /* Schedule any events set in initialization */
217 if (conn->internal->connect)
218 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
219 if (conn->internal->key_exchange)
220 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
222 /* Wait until this thread is terminated from the machine destructor */
223 return SILC_FSM_WAIT;
226 /* Connection machine main state. This handles various connection related
227 events, but not packet processing. It's done in dedicated packet
228 processing FSM thread. */
230 SILC_FSM_STATE(silc_client_connection_st_run)
232 SilcClientConnection conn = fsm_context;
233 SilcFSMThread thread;
235 /* Wait for events */
236 SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
239 thread = &conn->internal->event_thread;
241 if (conn->internal->connect) {
242 SILC_LOG_DEBUG(("Event: connect"));
243 conn->internal->connect = FALSE;
245 /*** Event: connect */
246 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
248 silc_fsm_start_sync(thread, silc_client_st_connect);
249 return SILC_FSM_CONTINUE;
252 if (conn->internal->key_exchange) {
253 SILC_LOG_DEBUG(("Event: key exchange"));
254 conn->internal->key_exchange = FALSE;
256 /*** Event: key exchange */
257 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
259 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
260 return SILC_FSM_CONTINUE;
263 if (conn->internal->rekeying) {
264 SILC_LOG_DEBUG(("Event: rekey"));
265 conn->internal->rekeying = FALSE;
268 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
270 silc_fsm_start_sync(thread, silc_client_st_rekey);
271 return SILC_FSM_CONTINUE;
274 if (conn->internal->disconnected) {
275 /** Event: disconnected */
276 SILC_LOG_DEBUG(("Event: disconnected"));
277 silc_fsm_next(fsm, silc_client_connection_st_close);
278 return SILC_FSM_YIELD;
283 return SILC_FSM_CONTINUE;
286 /* Packet processor thread. Each incoming packet is processed in FSM
287 thread in this state. The thread is run in the connection machine. */
289 SILC_FSM_STATE(silc_client_connection_st_packet)
291 SilcClientConnection conn = fsm_context;
292 SilcPacket packet = state_context;
294 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
296 switch (packet->type) {
298 case SILC_PACKET_PRIVATE_MESSAGE:
299 /** Private message */
300 silc_fsm_next(fsm, silc_client_private_message);
303 case SILC_PACKET_CHANNEL_MESSAGE:
304 /** Channel message */
305 silc_fsm_next(fsm, silc_client_channel_message);
308 case SILC_PACKET_FTP:
309 /* File transfer packet */
310 // silc_client_ftp(client, conn, packet);
313 case SILC_PACKET_CHANNEL_KEY:
315 silc_fsm_next(fsm, silc_client_channel_key);
318 case SILC_PACKET_COMMAND_REPLY:
320 silc_fsm_next(fsm, silc_client_command_reply);
323 case SILC_PACKET_NOTIFY:
325 silc_fsm_next(fsm, silc_client_notify);
328 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
329 /* Private message key indicator */
330 silc_fsm_next(fsm, silc_client_private_message_key);
333 case SILC_PACKET_DISCONNECT:
335 silc_fsm_next(fsm, silc_client_disconnect);
338 case SILC_PACKET_ERROR:
339 /* Error by server */
340 silc_fsm_next(fsm, silc_client_error);
343 case SILC_PACKET_KEY_AGREEMENT:
345 silc_fsm_next(fsm, silc_client_key_agreement);
348 case SILC_PACKET_COMMAND:
349 /** Command packet */
350 silc_fsm_next(fsm, silc_client_command);
353 case SILC_PACKET_NEW_ID:
355 silc_fsm_next(fsm, silc_client_new_id);
358 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
359 /* Reply to connection authentication request to resolve authentication
360 method from server. */
361 // silc_client_connection_auth_request(client, conn, packet);
364 case SILC_PACKET_REKEY:
365 /* Signal to start rekey */
366 conn->internal->rekey_responder = TRUE;
367 conn->internal->rekeying = TRUE;
368 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
370 silc_packet_free(packet);
371 return SILC_FSM_FINISH;
375 silc_packet_free(packet);
376 return SILC_FSM_FINISH;
380 return SILC_FSM_CONTINUE;
383 /* Disconnection event to close remote connection. We close the connection
384 and finish the connection machine in this state. The connection context
385 is deleted in the machine destructor. The connection callback must be
386 already called back to application before getting here. */
388 SILC_FSM_STATE(silc_client_connection_st_close)
390 SilcClientConnection conn = fsm_context;
391 SilcClientCommandContext cmd;
393 /* Finish running command threads. This will also finish waiting packet
394 thread, as they are always waiting for some command. If any thread is
395 waiting something else than command, they must be finished explicitly. */
396 if (silc_list_count(conn->internal->pending_commands)) {
397 SILC_LOG_DEBUG(("Finish pending commands"));
398 silc_list_start(conn->internal->pending_commands);
399 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
400 if (silc_fsm_is_started(&cmd->thread)) {
401 cmd->verbose = FALSE;
402 silc_fsm_continue_sync(&cmd->thread);
406 /* Give threads time to finish */
407 return SILC_FSM_YIELD;
410 /* Abort ongoing event */
411 if (conn->internal->op) {
412 SILC_LOG_DEBUG(("Abort event"));
413 silc_async_abort(conn->internal->op, NULL, NULL);
414 conn->internal->op = NULL;
417 /* If event thread is running, finish it. */
418 if (silc_fsm_is_started(&conn->internal->event_thread)) {
419 SILC_LOG_DEBUG(("Finish event thread"));
420 silc_fsm_continue_sync(&conn->internal->event_thread);
421 return SILC_FSM_YIELD;
424 SILC_LOG_DEBUG(("Closing remote connection"));
426 /* Close connection */
427 silc_packet_stream_destroy(conn->stream);
429 SILC_LOG_DEBUG(("Finishing connection machine"));
431 return SILC_FSM_FINISH;
434 /* Received error packet from server. Send it to application. */
436 SILC_FSM_STATE(silc_client_error)
438 SilcClientConnection conn = fsm_context;
439 SilcClient client = conn->client;
440 SilcPacket packet = state_context;
443 msg = silc_memdup(silc_buffer_data(&packet->buffer),
444 silc_buffer_len(&packet->buffer));
446 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
449 silc_packet_free(packet);
451 return SILC_FSM_FINISH;
454 /* Received disconnect packet from server. We close the connection and
455 send the disconnect message to application. */
457 SILC_FSM_STATE(silc_client_disconnect)
459 SilcClientConnection conn = fsm_context;
460 SilcClient client = conn->client;
461 SilcPacket packet = state_context;
463 char *message = NULL;
465 SILC_LOG_DEBUG(("Server disconnected"));
467 if (silc_buffer_len(&packet->buffer) < 1) {
468 silc_packet_free(packet);
469 return SILC_FSM_FINISH;
472 status = (SilcStatus)packet->buffer.data[0];
474 silc_buffer_pull(&packet->buffer, 1);
475 if (silc_buffer_len(&packet->buffer) > 1 &&
476 silc_utf8_valid(silc_buffer_data(&packet->buffer),
477 silc_buffer_len(&packet->buffer)))
478 message = silc_memdup(silc_buffer_data(&packet->buffer),
479 silc_buffer_len(&packet->buffer));
481 /* Call connection callback */
482 if (!conn->internal->callback_called)
483 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
484 message, conn->callback_context);
485 conn->internal->callback_called = TRUE;
488 silc_packet_free(packet);
490 /* Signal to close connection */
491 if (!conn->internal->disconnected) {
492 conn->internal->disconnected = TRUE;
493 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
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_SEMA_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_SEMA_POST(&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. */
567 static SilcClientConnection
568 silc_client_add_connection(SilcClient client,
569 SilcConnectionType conn_type,
570 SilcClientConnectionParams *params,
571 SilcPublicKey public_key,
572 SilcPrivateKey private_key,
573 char *remote_host, int port,
574 SilcClientConnectCallback callback,
577 SilcClientConnection conn;
578 SilcFSMThread thread;
583 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
585 conn = silc_calloc(1, sizeof(*conn));
589 conn->client = client;
590 conn->public_key = public_key;
591 conn->private_key = private_key;
592 conn->remote_host = strdup(remote_host);
593 conn->remote_port = port ? port : 706;
594 conn->type = conn_type;
595 conn->callback = callback;
596 conn->callback_context = context;
598 conn->internal = silc_calloc(1, sizeof(*conn->internal));
599 if (!conn->internal) {
603 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
604 silc_mutex_alloc(&conn->internal->lock);
605 silc_atomic_init16(&conn->internal->cmd_ident, 0);
607 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
609 silc_free(conn->internal);
615 conn->internal->params = *params;
616 if (!conn->internal->params.rekey_secs)
617 conn->internal->params.rekey_secs = 3600;
618 #ifndef SILC_DIST_INPLACE
619 if (conn->internal->params.rekey_secs < 300)
620 conn->internal->params.rekey_secs = 300;
621 #endif /* SILC_DIST_INPLACE */
623 conn->internal->verbose = TRUE;
624 silc_list_init(conn->internal->pending_commands,
625 struct SilcClientCommandContextStruct, next);
626 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
628 /* Allocate client, channel and serve caches */
629 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
631 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
633 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
635 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
636 !conn->internal->server_cache) {
637 silc_client_del_connection(client, conn);
641 conn->internal->ftp_sessions = silc_dlist_init();
643 /* Initialize our async operation so that application may abort us
644 while we're connecting. */
645 conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
647 if (!conn->internal->cop) {
648 silc_client_del_connection(client, conn);
652 /* Run the connection state machine. If threads are in use the machine
653 is always run in a real thread. */
654 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
655 silc_client_connection_finished, NULL,
656 client->internal->params->threads);
658 silc_client_del_connection(client, conn);
661 silc_fsm_set_state_context(thread, client);
662 silc_fsm_start(thread, silc_client_connection_st_start);
664 SILC_LOG_DEBUG(("New connection %p", conn));
665 silc_atomic_add_int16(&client->internal->conns, 1);
670 /* Deletes connection. This is always called from the connection machine
671 destructor. Do not call this directly other places. */
673 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
676 SilcIDCacheEntry entry;
677 SilcFSMThread thread;
679 SILC_LOG_DEBUG(("Freeing connection %p", conn));
681 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
683 /* Free all cache entries */
684 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
685 silc_list_start(list);
686 while ((entry = silc_list_get(list)))
687 silc_client_del_server(client, conn, entry->context);
689 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
690 silc_list_start(list);
691 while ((entry = silc_list_get(list))) {
692 silc_client_empty_channel(client, conn, entry->context);
693 silc_client_del_channel(client, conn, entry->context);
696 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
697 silc_list_start(list);
698 while ((entry = silc_list_get(list)))
699 silc_client_del_client(client, conn, entry->context);
703 if (conn->internal->client_cache)
704 silc_idcache_free(conn->internal->client_cache);
705 if (conn->internal->channel_cache)
706 silc_idcache_free(conn->internal->channel_cache);
707 if (conn->internal->server_cache)
708 silc_idcache_free(conn->internal->server_cache);
710 /* Free thread pool */
711 silc_list_start(conn->internal->thread_pool);
712 while ((thread = silc_list_get(conn->internal->thread_pool)))
713 silc_fsm_free(thread);
715 silc_free(conn->remote_host);
716 silc_buffer_free(conn->internal->local_idp);
717 silc_buffer_free(conn->internal->remote_idp);
718 silc_mutex_free(conn->internal->lock);
719 if (conn->internal->hash)
720 silc_hash_free(conn->internal->hash);
721 if (conn->internal->sha1hash)
722 silc_hash_free(conn->internal->sha1hash);
723 silc_atomic_uninit16(&conn->internal->cmd_ident);
725 silc_free(conn->internal);
726 memset(conn, 'F', sizeof(*conn));
730 /******************************* Client API *********************************/
732 /* Connects to remote server. This is the main routine used to connect
733 to remote SILC server. Performs key exchange also. Returns the
734 connection context to the connection callback. */
737 silc_client_connect_to_server(SilcClient client,
738 SilcClientConnectionParams *params,
739 SilcPublicKey public_key,
740 SilcPrivateKey private_key,
741 char *remote_host, int port,
742 SilcClientConnectCallback callback,
745 SilcClientConnection conn;
747 SILC_LOG_DEBUG(("Connecting to server"));
749 if (!client || !remote_host)
752 /* Add new connection */
753 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
754 public_key, private_key, remote_host,
755 port, callback, context);
757 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
761 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
762 "Connecting to port %d of server %s",
765 /* Signal connection machine to start connecting */
766 conn->internal->connect = TRUE;
767 return conn->internal->cop;
770 /* Connects to remote client. Performs key exchange also. Returns the
771 connection context to the connection callback. */
774 silc_client_connect_to_client(SilcClient client,
775 SilcClientConnectionParams *params,
776 SilcPublicKey public_key,
777 SilcPrivateKey private_key,
778 char *remote_host, int port,
779 SilcClientConnectCallback callback,
782 SilcClientConnection conn;
784 SILC_LOG_DEBUG(("Connecting to client"));
786 if (!client || !remote_host)
789 /* Add new connection */
790 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
791 public_key, private_key, remote_host,
792 port, callback, context);
794 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
798 /* Signal connection machine to start connecting */
799 conn->internal->connect = TRUE;
800 return conn->internal->cop;
803 /* Starts key exchange in the remote stream indicated by `stream'. This
804 creates the connection context and returns it in the connection callback. */
807 silc_client_key_exchange(SilcClient client,
808 SilcClientConnectionParams *params,
809 SilcPublicKey public_key,
810 SilcPrivateKey private_key,
812 SilcConnectionType conn_type,
813 SilcClientConnectCallback callback,
816 SilcClientConnection conn;
820 SILC_LOG_DEBUG(("Performing key exchange"));
822 if (!client || !stream)
825 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
826 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
827 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
831 /* Add new connection */
832 conn = silc_client_add_connection(client, conn_type, params,
833 public_key, private_key,
834 (char *)host, port, callback, context);
836 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
839 conn->stream = (void *)stream;
841 /* Signal connection to start key exchange */
842 conn->internal->key_exchange = TRUE;
843 return conn->internal->cop;
846 /* Closes remote connection */
848 void silc_client_close_connection(SilcClient client,
849 SilcClientConnection conn)
851 SILC_LOG_DEBUG(("Closing connection %p", conn));
853 /* Signal to close connection */
854 if (!conn->internal->disconnected) {
855 conn->internal->disconnected = TRUE;
856 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
861 /* Processes incoming connection authentication method request packet.
862 It is a reply to our previously sent request. The packet can be used
863 to resolve the authentication method for the current session if the
864 client does not know it beforehand. */
866 void silc_client_connection_auth_request(SilcClient client,
867 SilcClientConnection conn,
868 SilcPacketContext *packet)
870 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
871 SilcUInt16 conn_type, auth_meth;
874 /* If we haven't send our request then ignore this one. */
875 if (!conn->internal->connauth)
878 /* Parse the payload */
879 ret = silc_buffer_unformat(packet->buffer,
880 SILC_STR_UI_SHORT(&conn_type),
881 SILC_STR_UI_SHORT(&auth_meth),
884 auth_meth = SILC_AUTH_NONE;
886 /* Call the request callback to notify application for received
887 authentication method information. */
888 if (conn->internal->connauth->callback)
889 (*conn->internal->connauth->callback)(client, conn, auth_meth,
890 conn->internal->connauth->context);
892 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
894 silc_free(conn->internal->connauth);
895 conn->internal->connauth = NULL;
898 /* Timeout task callback called if the server does not reply to our
899 connection authentication method request in the specified time interval. */
901 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
903 SilcClientConnection conn = (SilcClientConnection)context;
904 SilcClient client = conn->client;
906 if (!conn->internal->connauth)
909 /* Call the request callback to notify application */
910 if (conn->internal->connauth->callback)
911 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
912 conn->internal->connauth->context);
914 silc_free(conn->internal->connauth);
915 conn->internal->connauth = NULL;
918 /* This function can be used to request the current authentication method
919 from the server. This may be called when connecting to the server
920 and the client library requests the authentication data from the
921 application. If the application does not know the current authentication
922 method it can request it from the server using this function.
923 The `callback' with `context' will be called after the server has
924 replied back with the current authentication method. */
927 silc_client_request_authentication_method(SilcClient client,
928 SilcClientConnection conn,
929 SilcConnectionAuthRequest callback,
932 SilcClientConnAuthRequest connauth;
935 assert(client && conn);
936 connauth = silc_calloc(1, sizeof(*connauth));
937 connauth->callback = callback;
938 connauth->context = context;
940 if (conn->internal->connauth)
941 silc_free(conn->internal->connauth);
943 conn->internal->connauth = connauth;
945 /* Assemble the request packet and send it to the server */
946 packet = silc_buffer_alloc(4);
947 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
948 silc_buffer_format(packet,
949 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
950 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
952 silc_client_packet_send(client, conn->sock,
953 SILC_PACKET_CONNECTION_AUTH_REQUEST,
955 packet->data, packet->len, FALSE);
956 silc_buffer_free(packet);
958 /* Register a timeout in case server does not reply anything back. */
960 silc_schedule_task_add(client->schedule, conn->sock->sock,
961 silc_client_request_authentication_method_timeout,
963 client->internal->params->connauth_request_secs, 0,
964 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
968 /* Allocates new client object. This has to be done before client may
969 work. After calling this one must call silc_client_init to initialize
970 the client. The `application' is application specific user data pointer
971 and caller must free it. */
973 SilcClient silc_client_alloc(SilcClientOperations *ops,
974 SilcClientParams *params,
976 const char *version_string)
978 SilcClient new_client;
980 new_client = silc_calloc(1, sizeof(*new_client));
983 new_client->application = application;
985 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
986 if (!new_client->internal) {
987 silc_free(new_client);
990 new_client->internal->ops = ops;
991 new_client->internal->params =
992 silc_calloc(1, sizeof(*new_client->internal->params));
994 version_string = silc_version_string;
995 new_client->internal->silc_client_version = strdup(version_string);
998 memcpy(new_client->internal->params, params, sizeof(*params));
1000 if (!new_client->internal->params->connauth_request_secs)
1001 new_client->internal->params->connauth_request_secs = 2;
1003 new_client->internal->params->
1004 nickname_format[sizeof(new_client->internal->
1005 params->nickname_format) - 1] = 0;
1007 silc_atomic_init16(&new_client->internal->conns, 0);
1012 /* Frees client object and its internals. */
1014 void silc_client_free(SilcClient client)
1016 silc_schedule_uninit(client->schedule);
1019 silc_rng_free(client->rng);
1021 if (!client->internal->params->dont_register_crypto_library) {
1022 silc_cipher_unregister_all();
1023 silc_pkcs_unregister_all();
1024 silc_hash_unregister_all();
1025 silc_hmac_unregister_all();
1028 silc_atomic_uninit16(&client->internal->conns);
1029 silc_free(client->username);
1030 silc_free(client->hostname);
1031 silc_free(client->realname);
1032 silc_free(client->internal->params);
1033 silc_free(client->internal->silc_client_version);
1034 silc_free(client->internal);
1038 /* Initializes the client. This makes all the necessary steps to make
1039 the client ready to be run. One must call silc_client_run to run the
1040 client. Returns FALSE if error occured, TRUE otherwise. */
1042 SilcBool silc_client_init(SilcClient client, const char *username,
1043 const char *hostname, const char *realname,
1044 SilcClientRunning running, void *context)
1046 SILC_LOG_DEBUG(("Initializing client"));
1051 if (!username || !hostname) {
1052 SILC_LOG_ERROR(("Username and hostname must be given to "
1053 "silc_client_init"));
1057 realname = username;
1059 /* Validate essential strings */
1060 if (!silc_identifier_verify(username, strlen(username),
1061 SILC_STRING_UTF8, 128)) {
1062 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1066 if (!silc_identifier_verify(hostname, strlen(hostname),
1067 SILC_STRING_UTF8, 256)) {
1068 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1072 if (!silc_utf8_valid(realname, strlen(realname))) {
1073 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1078 /* Take the name strings */
1079 client->username = strdup(username);
1080 client->hostname = strdup(hostname);
1081 client->realname = strdup(realname);
1082 if (!username || !hostname || !realname)
1085 if (!client->internal->params->dont_register_crypto_library) {
1086 /* Initialize the crypto library. If application has done this already
1087 this has no effect. Also, we will not be overriding something
1088 application might have registered earlier. */
1089 silc_cipher_register_default();
1090 silc_pkcs_register_default();
1091 silc_hash_register_default();
1092 silc_hmac_register_default();
1095 /* Initialize random number generator */
1096 client->rng = silc_rng_alloc();
1099 silc_rng_init(client->rng);
1100 silc_rng_global_init(client->rng);
1102 /* Initialize the scheduler */
1103 client->schedule = silc_schedule_init(0, client);
1104 if (!client->schedule)
1107 /* Allocate client lock */
1108 silc_mutex_alloc(&client->internal->lock);
1110 /* Register commands */
1111 silc_client_commands_register(client);
1113 /* Start packet engine */
1114 client->internal->packet_engine =
1115 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1117 if (!client->internal->packet_engine)
1120 /* Initialize and start the client FSM */
1121 client->internal->running = running;
1122 client->internal->running_context = context;
1123 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1124 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1125 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1127 /* Signal the application when we are running */
1128 client->internal->run_callback = TRUE;
1129 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1134 /* Starts the SILC client FSM machine and blocks here. When this returns
1135 the client has ended. */
1137 void silc_client_run(SilcClient client)
1139 SILC_LOG_DEBUG(("Starting SILC client"));
1141 /* Run the scheduler */
1142 silc_schedule(client->schedule);
1145 /* Call scheduler one iteration and return. */
1147 void silc_client_run_one(SilcClient client)
1149 if (silc_fsm_is_started(&client->internal->fsm))
1150 silc_schedule_one(client->schedule, 0);
1153 /* Stops the client. This is called to stop the client and thus to stop
1156 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1159 SILC_LOG_DEBUG(("Stopping client"));
1161 client->internal->running = (SilcClientRunning)stopped;
1162 client->internal->running_context = context;
1164 /* Signal to stop */
1165 client->internal->stop = TRUE;
1166 SILC_FSM_SEMA_POST(&client->internal->wait_event);