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));
188 conn->internal->aborted = TRUE;
190 /* Signal to close connection */
191 if (!conn->internal->disconnected) {
192 conn->internal->disconnected = TRUE;
193 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
197 /************************** Connection's machine ****************************/
199 /* Start the connection's state machine. If threads are in use the machine
200 is always executed in a real thread. */
202 SILC_FSM_STATE(silc_client_connection_st_start)
204 SilcClientConnection conn = fsm_context;
207 /* Take scheduler for connection */
208 conn->internal->schedule = silc_fsm_get_schedule(fsm);
210 /*** Run connection machine */
211 connfsm = &conn->internal->fsm;
212 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
213 fsm, conn->internal->schedule);
214 silc_fsm_sema_init(&conn->internal->wait_event, connfsm, 0);
215 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
217 /* Schedule any events set in initialization */
218 if (conn->internal->connect)
219 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
220 if (conn->internal->key_exchange)
221 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
223 /* Wait until this thread is terminated from the machine destructor */
224 return SILC_FSM_WAIT;
227 /* Connection machine main state. This handles various connection related
228 events, but not packet processing. It's done in dedicated packet
229 processing FSM thread. */
231 SILC_FSM_STATE(silc_client_connection_st_run)
233 SilcClientConnection conn = fsm_context;
234 SilcFSMThread thread;
236 /* Wait for events */
237 SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
240 thread = &conn->internal->event_thread;
242 if (conn->internal->connect) {
243 SILC_LOG_DEBUG(("Event: connect"));
244 conn->internal->connect = FALSE;
246 /*** Event: connect */
247 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
249 silc_fsm_start_sync(thread, silc_client_st_connect);
250 return SILC_FSM_CONTINUE;
253 if (conn->internal->key_exchange) {
254 SILC_LOG_DEBUG(("Event: key exchange"));
255 conn->internal->key_exchange = FALSE;
257 /*** Event: key exchange */
258 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
260 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
261 return SILC_FSM_CONTINUE;
264 if (conn->internal->rekeying) {
265 SILC_LOG_DEBUG(("Event: rekey"));
266 conn->internal->rekeying = FALSE;
269 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
271 silc_fsm_start_sync(thread, silc_client_st_rekey);
272 return SILC_FSM_CONTINUE;
275 if (conn->internal->disconnected) {
276 /** Event: disconnected */
277 SILC_LOG_DEBUG(("Event: disconnected"));
278 silc_fsm_next(fsm, silc_client_connection_st_close);
279 return SILC_FSM_YIELD;
284 return SILC_FSM_CONTINUE;
287 /* Packet processor thread. Each incoming packet is processed in FSM
288 thread in this state. The thread is run in the connection machine. */
290 SILC_FSM_STATE(silc_client_connection_st_packet)
292 SilcClientConnection conn = fsm_context;
293 SilcPacket packet = state_context;
295 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
297 switch (packet->type) {
299 case SILC_PACKET_PRIVATE_MESSAGE:
300 /** Private message */
301 silc_fsm_next(fsm, silc_client_private_message);
304 case SILC_PACKET_CHANNEL_MESSAGE:
305 /** Channel message */
306 silc_fsm_next(fsm, silc_client_channel_message);
309 case SILC_PACKET_FTP:
310 /* File transfer packet */
311 // silc_client_ftp(client, conn, packet);
314 case SILC_PACKET_CHANNEL_KEY:
316 silc_fsm_next(fsm, silc_client_channel_key);
319 case SILC_PACKET_COMMAND_REPLY:
321 silc_fsm_next(fsm, silc_client_command_reply);
324 case SILC_PACKET_NOTIFY:
326 silc_fsm_next(fsm, silc_client_notify);
329 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
330 /* Private message key indicator */
331 silc_fsm_next(fsm, silc_client_private_message_key);
334 case SILC_PACKET_DISCONNECT:
336 silc_fsm_next(fsm, silc_client_disconnect);
339 case SILC_PACKET_ERROR:
340 /* Error by server */
341 silc_fsm_next(fsm, silc_client_error);
344 case SILC_PACKET_KEY_AGREEMENT:
346 silc_fsm_next(fsm, silc_client_key_agreement);
349 case SILC_PACKET_COMMAND:
350 /** Command packet */
351 silc_fsm_next(fsm, silc_client_command);
354 case SILC_PACKET_NEW_ID:
356 silc_fsm_next(fsm, silc_client_new_id);
359 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
360 /* Reply to connection authentication request to resolve authentication
361 method from server. */
362 // silc_client_connection_auth_request(client, conn, packet);
365 case SILC_PACKET_REKEY:
366 /* Signal to start rekey */
367 conn->internal->rekey_responder = TRUE;
368 conn->internal->rekeying = TRUE;
369 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
371 silc_packet_free(packet);
372 return SILC_FSM_FINISH;
376 silc_packet_free(packet);
377 return SILC_FSM_FINISH;
381 return SILC_FSM_CONTINUE;
384 /* Disconnection event to close remote connection. We close the connection
385 and finish the connection machine in this state. The connection context
386 is deleted in the machine destructor. The connection callback must be
387 already called back to application before getting here. */
389 SILC_FSM_STATE(silc_client_connection_st_close)
391 SilcClientConnection conn = fsm_context;
392 SilcClientCommandContext cmd;
394 /* Finish running command threads. This will also finish waiting packet
395 thread, as they are always waiting for some command. If any thread is
396 waiting something else than command, they must be finished explicitly. */
397 if (silc_list_count(conn->internal->pending_commands)) {
398 SILC_LOG_DEBUG(("Finish pending commands"));
399 silc_list_start(conn->internal->pending_commands);
400 while ((cmd = silc_list_get(conn->internal->pending_commands))) {
401 if (silc_fsm_is_started(&cmd->thread)) {
402 cmd->verbose = FALSE;
403 silc_fsm_continue_sync(&cmd->thread);
407 /* Give threads time to finish */
408 return SILC_FSM_YIELD;
411 /* Abort ongoing events */
412 if (conn->internal->op) {
413 SILC_LOG_DEBUG(("Abort event"));
414 silc_async_abort(conn->internal->op, NULL, NULL);
415 conn->internal->op = NULL;
418 /* If event thread is running, finish it. */
419 if (silc_fsm_is_started(&conn->internal->event_thread)) {
420 SILC_LOG_DEBUG(("Finish event thread"));
421 silc_fsm_continue_sync(&conn->internal->event_thread);
422 return SILC_FSM_YIELD;
425 SILC_LOG_DEBUG(("Closing remote connection"));
427 /* Close connection */
428 silc_packet_stream_destroy(conn->stream);
430 SILC_LOG_DEBUG(("Finishing connection machine"));
432 return SILC_FSM_FINISH;
435 /* Received error packet from server. Send it to application. */
437 SILC_FSM_STATE(silc_client_error)
439 SilcClientConnection conn = fsm_context;
440 SilcClient client = conn->client;
441 SilcPacket packet = state_context;
444 msg = silc_memdup(silc_buffer_data(&packet->buffer),
445 silc_buffer_len(&packet->buffer));
447 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
450 silc_packet_free(packet);
452 return SILC_FSM_FINISH;
455 /* Received disconnect packet from server. We close the connection and
456 send the disconnect message to application. */
458 SILC_FSM_STATE(silc_client_disconnect)
460 SilcClientConnection conn = fsm_context;
461 SilcClient client = conn->client;
462 SilcPacket packet = state_context;
464 char *message = NULL;
466 SILC_LOG_DEBUG(("Server disconnected"));
468 if (silc_buffer_len(&packet->buffer) < 1) {
469 silc_packet_free(packet);
470 return SILC_FSM_FINISH;
473 status = (SilcStatus)packet->buffer.data[0];
475 silc_buffer_pull(&packet->buffer, 1);
476 if (silc_buffer_len(&packet->buffer) > 1 &&
477 silc_utf8_valid(silc_buffer_data(&packet->buffer),
478 silc_buffer_len(&packet->buffer)))
479 message = silc_memdup(silc_buffer_data(&packet->buffer),
480 silc_buffer_len(&packet->buffer));
482 /* Call connection callback */
483 if (!conn->internal->callback_called)
484 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
485 message, conn->callback_context);
486 conn->internal->callback_called = TRUE;
489 silc_packet_free(packet);
491 /* Signal to close connection */
492 if (!conn->internal->disconnected) {
493 conn->internal->disconnected = TRUE;
494 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
497 return SILC_FSM_FINISH;
500 /*************************** Main client machine ****************************/
502 /* The client's main state where we wait for various events */
504 SILC_FSM_STATE(silc_client_st_run)
506 SilcClient client = fsm_context;
508 /* Wait for events */
509 SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
513 if (client->internal->run_callback && client->internal->running) {
514 /* Call running callbcak back to application */
515 SILC_LOG_DEBUG(("We are up, call running callback"));
516 client->internal->run_callback = FALSE;
517 client->internal->running(client, client->internal->running_context);
518 return SILC_FSM_CONTINUE;
521 if (client->internal->connection_closed) {
522 /* A connection finished */
523 SILC_LOG_DEBUG(("Event: connection closed"));
524 client->internal->connection_closed = FALSE;
525 if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
526 client->internal->stop)
527 SILC_FSM_SEMA_POST(&client->internal->wait_event);
528 return SILC_FSM_CONTINUE;
531 if (client->internal->stop) {
532 /* Stop client libarry. If we have running connections, wait until
533 they finish first. */
534 SILC_LOG_DEBUG(("Event: stop"));
535 if (silc_atomic_get_int16(&client->internal->conns) == 0)
536 silc_fsm_next(fsm, silc_client_st_stop);
537 return SILC_FSM_CONTINUE;
542 return SILC_FSM_CONTINUE;
545 /* Stop event. Stops the client library. */
547 SILC_FSM_STATE(silc_client_st_stop)
549 SilcClient client = fsm_context;
551 SILC_LOG_DEBUG(("Client stopped"));
554 silc_schedule_stop(client->schedule);
555 silc_client_commands_unregister(client);
557 /* Call stopped callback to application */
558 if (client->internal->running)
559 client->internal->running(client, client->internal->running_context);
561 return SILC_FSM_FINISH;
564 /******************************* Private API ********************************/
566 /* Adds new connection. Creates the connection context and returns it. */
568 static SilcClientConnection
569 silc_client_add_connection(SilcClient client,
570 SilcConnectionType conn_type,
571 SilcClientConnectionParams *params,
572 SilcPublicKey public_key,
573 SilcPrivateKey private_key,
574 char *remote_host, int port,
575 SilcClientConnectCallback callback,
578 SilcClientConnection conn;
579 SilcFSMThread thread;
584 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
586 conn = silc_calloc(1, sizeof(*conn));
590 conn->client = client;
591 conn->public_key = public_key;
592 conn->private_key = private_key;
593 conn->remote_host = strdup(remote_host);
594 conn->remote_port = port ? port : 706;
595 conn->type = conn_type;
596 conn->callback = callback;
597 conn->callback_context = context;
599 conn->internal = silc_calloc(1, sizeof(*conn->internal));
600 if (!conn->internal) {
604 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
605 silc_mutex_alloc(&conn->internal->lock);
606 silc_atomic_init16(&conn->internal->cmd_ident, 0);
608 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
610 silc_free(conn->internal);
616 conn->internal->params = *params;
617 if (!conn->internal->params.rekey_secs)
618 conn->internal->params.rekey_secs = 3600;
619 #ifndef SILC_DIST_INPLACE
620 if (conn->internal->params.rekey_secs < 300)
621 conn->internal->params.rekey_secs = 300;
622 #endif /* SILC_DIST_INPLACE */
624 conn->internal->verbose = TRUE;
625 silc_list_init(conn->internal->pending_commands,
626 struct SilcClientCommandContextStruct, next);
627 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
629 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 /* Initiatlize our async operation so that application may abort us
644 while were 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));
731 /******************************* Client API *********************************/
733 /* Connects to remote server. This is the main routine used to connect
734 to remote SILC server. Performs key exchange also. Returns the
735 connection context to the connection callback. */
738 silc_client_connect_to_server(SilcClient client,
739 SilcClientConnectionParams *params,
740 SilcPublicKey public_key,
741 SilcPrivateKey private_key,
742 char *remote_host, int port,
743 SilcClientConnectCallback callback,
746 SilcClientConnection conn;
748 if (!client || !remote_host)
751 /* Add new connection */
752 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
753 public_key, private_key, remote_host,
754 port, callback, context);
756 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
760 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
761 "Connecting to port %d of server %s",
764 /* Signal connection machine to start connecting */
765 conn->internal->connect = TRUE;
766 return conn->internal->cop;
769 /* Connects to remote client. Performs key exchange also. Returns the
770 connection context to the connection callback. */
773 silc_client_connect_to_client(SilcClient client,
774 SilcClientConnectionParams *params,
775 SilcPublicKey public_key,
776 SilcPrivateKey private_key,
777 char *remote_host, int port,
778 SilcClientConnectCallback callback,
781 SilcClientConnection conn;
783 if (!client || !remote_host)
786 /* Add new connection */
787 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
788 public_key, private_key, remote_host,
789 port, callback, context);
791 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
795 /* Signal connection machine to start connecting */
796 conn->internal->connect = TRUE;
797 return conn->internal->cop;
800 /* Starts key exchange in the remote stream indicated by `stream'. This
801 creates the connection context and returns it in the connection callback. */
804 silc_client_key_exchange(SilcClient client,
805 SilcClientConnectionParams *params,
806 SilcPublicKey public_key,
807 SilcPrivateKey private_key,
809 SilcConnectionType conn_type,
810 SilcClientConnectCallback callback,
813 SilcClientConnection conn;
817 if (!client || !stream)
820 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
821 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
822 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
826 /* Add new connection */
827 conn = silc_client_add_connection(client, conn_type, params,
828 public_key, private_key,
829 (char *)host, port, callback, context);
831 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
834 conn->stream = (void *)stream;
836 /* Signal connection to start key exchange */
837 conn->internal->key_exchange = TRUE;
838 return conn->internal->cop;
841 /* Closes remote connection */
843 void silc_client_close_connection(SilcClient client,
844 SilcClientConnection conn)
846 SILC_LOG_DEBUG(("Closing connection %p", conn));
848 /* Signal to close connection */
849 if (!conn->internal->disconnected) {
850 conn->internal->disconnected = TRUE;
851 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
856 /* Finalizes the connection to the remote SILC server. This is called
857 after authentication protocol has been completed. This send our
858 user information to the server to receive our client ID from
861 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
863 SilcProtocol protocol = (SilcProtocol)context;
864 SilcClientConnAuthInternalContext *ctx =
865 (SilcClientConnAuthInternalContext *)protocol->context;
866 SilcClient client = (SilcClient)ctx->client;
867 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
870 SILC_LOG_DEBUG(("Start"));
872 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
873 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
874 /* Error occured during protocol */
875 SILC_LOG_DEBUG(("Error during authentication protocol"));
876 ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
880 if (conn->internal->params.detach_data) {
881 /* Send RESUME_CLIENT packet to the server, which is used to resume
882 old detached session back. */
884 SilcClientID *old_client_id;
885 unsigned char *old_id;
886 SilcUInt16 old_id_len;
888 if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
889 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
893 old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
894 if (!old_client_id) {
896 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
900 /* Generate authentication data that server will verify */
901 auth = silc_auth_public_key_auth_generate(client->public_key,
904 conn->internal->hash,
905 old_client_id, SILC_ID_CLIENT);
907 silc_free(old_client_id);
909 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
913 packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
914 silc_buffer_format(packet,
915 SILC_STR_UI_SHORT(old_id_len),
916 SILC_STR_UI_XNSTRING(old_id, old_id_len),
917 SILC_STR_UI_XNSTRING(auth->data, auth->len),
920 /* Send the packet */
921 silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
923 packet->data, packet->len, TRUE);
924 silc_buffer_free(packet);
925 silc_buffer_free(auth);
926 silc_free(old_client_id);
929 /* Send NEW_CLIENT packet to the server. We will become registered
930 to the SILC network after sending this packet and we will receive
931 client ID from the server. */
932 packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
933 strlen(client->realname));
934 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
935 silc_buffer_format(packet,
936 SILC_STR_UI_SHORT(strlen(client->username)),
937 SILC_STR_UI_XNSTRING(client->username,
938 strlen(client->username)),
939 SILC_STR_UI_SHORT(strlen(client->realname)),
940 SILC_STR_UI_XNSTRING(client->realname,
941 strlen(client->realname)),
944 /* Send the packet */
945 silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
947 packet->data, packet->len, TRUE);
948 silc_buffer_free(packet);
951 /* Save remote ID. */
952 conn->remote_id = ctx->dest_id;
953 conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
954 conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
956 /* Register re-key timeout */
957 conn->internal->rekey->timeout = client->internal->params->rekey_secs;
958 conn->internal->rekey->context = (void *)client;
959 silc_schedule_task_add(client->schedule, conn->sock->sock,
960 silc_client_rekey_callback,
961 (void *)conn->sock, conn->internal->rekey->timeout, 0,
962 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
964 silc_protocol_free(protocol);
965 silc_free(ctx->auth_data);
966 silc_socket_free(ctx->sock);
968 conn->sock->protocol = NULL;
972 silc_protocol_free(protocol);
973 silc_free(ctx->auth_data);
974 silc_free(ctx->dest_id);
975 conn->sock->protocol = NULL;
976 silc_socket_free(ctx->sock);
978 /* Notify application of failure */
979 silc_schedule_task_add(client->schedule, ctx->sock->sock,
980 silc_client_connect_failure_auth, ctx,
981 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
984 /* Client session resuming callback. If the session was resumed
985 this callback is called after the resuming is completed. This
986 will call the `connect' client operation to the application
987 since it has not been called yet. */
989 static void silc_client_resume_session_cb(SilcClient client,
990 SilcClientConnection conn,
996 /* Notify application that connection is created to server */
997 client->internal->ops->connected(client, conn, success ?
998 SILC_CLIENT_CONN_SUCCESS_RESUME :
999 SILC_CLIENT_CONN_ERROR_RESUME);
1002 /* Issue INFO command to fetch the real server name and server
1003 information and other stuff. */
1004 silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
1005 silc_client_command_reply_info_i, 0,
1007 sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
1008 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
1009 conn->cmd_ident, 1, 2, sidp->data, sidp->len);
1010 silc_buffer_free(sidp);
1014 /* Processes incoming connection authentication method request packet.
1015 It is a reply to our previously sent request. The packet can be used
1016 to resolve the authentication method for the current session if the
1017 client does not know it beforehand. */
1019 void silc_client_connection_auth_request(SilcClient client,
1020 SilcClientConnection conn,
1021 SilcPacketContext *packet)
1023 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1024 SilcUInt16 conn_type, auth_meth;
1027 /* If we haven't send our request then ignore this one. */
1028 if (!conn->internal->connauth)
1031 /* Parse the payload */
1032 ret = silc_buffer_unformat(packet->buffer,
1033 SILC_STR_UI_SHORT(&conn_type),
1034 SILC_STR_UI_SHORT(&auth_meth),
1037 auth_meth = SILC_AUTH_NONE;
1039 /* Call the request callback to notify application for received
1040 authentication method information. */
1041 if (conn->internal->connauth->callback)
1042 (*conn->internal->connauth->callback)(client, conn, auth_meth,
1043 conn->internal->connauth->context);
1045 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
1047 silc_free(conn->internal->connauth);
1048 conn->internal->connauth = NULL;
1051 /* Timeout task callback called if the server does not reply to our
1052 connection authentication method request in the specified time interval. */
1054 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
1056 SilcClientConnection conn = (SilcClientConnection)context;
1057 SilcClient client = conn->client;
1059 if (!conn->internal->connauth)
1062 /* Call the request callback to notify application */
1063 if (conn->internal->connauth->callback)
1064 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
1065 conn->internal->connauth->context);
1067 silc_free(conn->internal->connauth);
1068 conn->internal->connauth = NULL;
1071 /* This function can be used to request the current authentication method
1072 from the server. This may be called when connecting to the server
1073 and the client library requests the authentication data from the
1074 application. If the application does not know the current authentication
1075 method it can request it from the server using this function.
1076 The `callback' with `context' will be called after the server has
1077 replied back with the current authentication method. */
1080 silc_client_request_authentication_method(SilcClient client,
1081 SilcClientConnection conn,
1082 SilcConnectionAuthRequest callback,
1085 SilcClientConnAuthRequest connauth;
1088 assert(client && conn);
1089 connauth = silc_calloc(1, sizeof(*connauth));
1090 connauth->callback = callback;
1091 connauth->context = context;
1093 if (conn->internal->connauth)
1094 silc_free(conn->internal->connauth);
1096 conn->internal->connauth = connauth;
1098 /* Assemble the request packet and send it to the server */
1099 packet = silc_buffer_alloc(4);
1100 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
1101 silc_buffer_format(packet,
1102 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
1103 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
1105 silc_client_packet_send(client, conn->sock,
1106 SILC_PACKET_CONNECTION_AUTH_REQUEST,
1107 NULL, 0, NULL, NULL,
1108 packet->data, packet->len, FALSE);
1109 silc_buffer_free(packet);
1111 /* Register a timeout in case server does not reply anything back. */
1113 silc_schedule_task_add(client->schedule, conn->sock->sock,
1114 silc_client_request_authentication_method_timeout,
1116 client->internal->params->connauth_request_secs, 0,
1117 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1122 /* Allocates new client object. This has to be done before client may
1123 work. After calling this one must call silc_client_init to initialize
1124 the client. The `application' is application specific user data pointer
1125 and caller must free it. */
1127 SilcClient silc_client_alloc(SilcClientOperations *ops,
1128 SilcClientParams *params,
1130 const char *version_string)
1132 SilcClient new_client;
1134 new_client = silc_calloc(1, sizeof(*new_client));
1137 new_client->application = application;
1139 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
1140 if (!new_client->internal) {
1141 silc_free(new_client);
1144 new_client->internal->ops = ops;
1145 new_client->internal->params =
1146 silc_calloc(1, sizeof(*new_client->internal->params));
1147 if (!version_string)
1148 version_string = silc_version_string;
1149 new_client->internal->silc_client_version = strdup(version_string);
1152 memcpy(new_client->internal->params, params, sizeof(*params));
1154 if (!new_client->internal->params->connauth_request_secs)
1155 new_client->internal->params->connauth_request_secs = 2;
1157 new_client->internal->params->
1158 nickname_format[sizeof(new_client->internal->
1159 params->nickname_format) - 1] = 0;
1161 silc_atomic_init16(&new_client->internal->conns, 0);
1166 /* Frees client object and its internals. */
1168 void silc_client_free(SilcClient client)
1170 silc_schedule_uninit(client->schedule);
1173 silc_rng_free(client->rng);
1175 if (!client->internal->params->dont_register_crypto_library) {
1176 silc_cipher_unregister_all();
1177 silc_pkcs_unregister_all();
1178 silc_hash_unregister_all();
1179 silc_hmac_unregister_all();
1182 silc_atomic_uninit16(&client->internal->conns);
1183 silc_free(client->username);
1184 silc_free(client->hostname);
1185 silc_free(client->realname);
1186 silc_free(client->internal->params);
1187 silc_free(client->internal->silc_client_version);
1188 silc_free(client->internal);
1192 /* Initializes the client. This makes all the necessary steps to make
1193 the client ready to be run. One must call silc_client_run to run the
1194 client. Returns FALSE if error occured, TRUE otherwise. */
1196 SilcBool silc_client_init(SilcClient client, const char *username,
1197 const char *hostname, const char *realname,
1198 SilcClientRunning running, void *context)
1200 SILC_LOG_DEBUG(("Initializing client"));
1205 if (!username || !hostname) {
1206 SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1207 "silc_client_init"));
1211 realname = username;
1213 /* Validate essential strings */
1214 if (!silc_identifier_verify(username, strlen(username),
1215 SILC_STRING_UTF8, 128)) {
1216 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1220 if (!silc_identifier_verify(hostname, strlen(hostname),
1221 SILC_STRING_UTF8, 256)) {
1222 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1226 if (!silc_utf8_valid(realname, strlen(realname))) {
1227 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1232 /* Take the name strings */
1233 client->username = strdup(username);
1234 client->hostname = strdup(hostname);
1235 client->realname = strdup(realname);
1236 if (!username || !hostname || !realname)
1239 if (!client->internal->params->dont_register_crypto_library) {
1240 /* Initialize the crypto library. If application has done this already
1241 this has no effect. Also, we will not be overriding something
1242 application might have registered earlier. */
1243 silc_cipher_register_default();
1244 silc_pkcs_register_default();
1245 silc_hash_register_default();
1246 silc_hmac_register_default();
1249 /* Initialize random number generator */
1250 client->rng = silc_rng_alloc();
1253 silc_rng_init(client->rng);
1254 silc_rng_global_init(client->rng);
1256 /* Initialize the scheduler */
1257 client->schedule = silc_schedule_init(0, client);
1258 if (!client->schedule)
1261 /* Start packet engine */
1262 client->internal->packet_engine =
1263 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1265 if (!client->internal->packet_engine)
1268 /* Allocate client lock */
1269 silc_mutex_alloc(&client->internal->lock);
1271 /* Register commands */
1272 silc_client_commands_register(client);
1274 /* Initialize and start the client FSM */
1275 client->internal->running = running;
1276 client->internal->running_context = context;
1277 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1278 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1279 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1281 /* Signal the application when we are running */
1282 client->internal->run_callback = TRUE;
1283 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1288 /* Starts the SILC client FSM machine and blocks here. When this returns
1289 the client has ended. */
1291 void silc_client_run(SilcClient client)
1293 SILC_LOG_DEBUG(("Starting SILC client"));
1295 /* Run the scheduler */
1296 silc_schedule(client->schedule);
1299 /* Call scheduler one iteration and return. This cannot be called if threads
1302 void silc_client_run_one(SilcClient client)
1304 if (silc_fsm_is_started(&client->internal->fsm))
1305 silc_schedule_one(client->schedule, 0);
1308 /* Stops the client. This is called to stop the client and thus to stop
1311 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1314 SILC_LOG_DEBUG(("Stopping client"));
1316 client->internal->running = (SilcClientRunning)stopped;
1317 client->internal->running_context = context;
1319 /* Signal to stop */
1320 client->internal->stop = TRUE;
1321 SILC_FSM_SEMA_POST(&client->internal->wait_event);