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 /* Delete connection */
41 silc_client_del_connection(conn->client, conn);
43 /* Finish the thread were this machine was running */
44 silc_fsm_finish(thread);
47 /* Packet FSM thread destructor */
49 static void silc_client_packet_destructor(SilcFSMThread thread,
51 void *destructor_context)
53 SilcClientConnection conn = thread_context;
55 /* Add thread back to thread pool */
56 silc_list_add(conn->internal->thread_pool, thread);
57 if (silc_list_count(conn->internal->thread_pool) == 1)
58 silc_list_start(conn->internal->thread_pool);
61 /* Packet engine callback to receive a packet */
63 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
64 SilcPacketStream stream,
66 void *callback_context,
69 SilcClientConnection conn = stream_context;
72 /* Packets we do not handle */
73 switch (packet->type) {
74 case SILC_PACKET_HEARTBEAT:
75 case SILC_PACKET_SUCCESS:
76 case SILC_PACKET_FAILURE:
77 case SILC_PACKET_REJECT:
78 case SILC_PACKET_KEY_EXCHANGE:
79 case SILC_PACKET_KEY_EXCHANGE_1:
80 case SILC_PACKET_KEY_EXCHANGE_2:
81 case SILC_PACKET_REKEY_DONE:
82 case SILC_PACKET_CONNECTION_AUTH:
83 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
88 /* Get packet processing thread */
89 thread = silc_list_get(conn->internal->thread_pool);
91 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
92 silc_client_packet_destructor, NULL, FALSE);
96 silc_list_del(conn->internal->thread_pool, thread);
97 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
98 silc_client_packet_destructor, NULL, FALSE);
101 /* Process packet in thread */
102 silc_fsm_set_state_context(thread, packet);
103 silc_fsm_start_sync(thread, silc_client_connection_st_packet);
108 /* Packet engine callback to indicate end of stream */
110 static void silc_client_packet_eos(SilcPacketEngine engine,
111 SilcPacketStream stream,
112 void *callback_context,
113 void *stream_context)
115 SilcClientConnection conn = stream_context;
116 SilcClient client = conn->client;
118 SILC_LOG_DEBUG(("Remote disconnected connection"));
120 /* Call connection callback */
121 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, 0, NULL,
122 conn->callback_context);
124 /* Signal to close connection */
125 if (!conn->internal->disconnected) {
126 conn->internal->disconnected = TRUE;
127 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
131 /* Packet engine callback to indicate error */
133 static void silc_client_packet_error(SilcPacketEngine engine,
134 SilcPacketStream stream,
135 SilcPacketError error,
136 void *callback_context,
137 void *stream_context)
142 /* Packet stream callbacks */
143 static SilcPacketCallbacks silc_client_stream_cbs =
145 silc_client_packet_receive,
146 silc_client_packet_eos,
147 silc_client_packet_error
152 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
153 void *destructor_context)
159 /************************** Connection's machine ****************************/
161 /* Start the connection's state machine. If threads are in use the machine
162 is always executed in a real thread. */
164 SILC_FSM_STATE(silc_client_connection_st_start)
166 SilcClientConnection conn = fsm_context;
169 /* Take scheduler for connection */
170 conn->internal->schedule = silc_fsm_get_schedule(fsm);
172 /*** Run connection machine */
173 connfsm = &conn->internal->fsm;
174 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
175 fsm, conn->internal->schedule);
176 silc_fsm_sema_init(&conn->internal->wait_event, connfsm, 0);
177 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
179 /* Schedule any events set in initialization */
180 if (conn->internal->connect)
181 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
182 if (conn->internal->key_exchange)
183 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
185 /* Wait until this thread is terminated from the machine destructor */
186 return SILC_FSM_WAIT;
189 /* Connection machine main state. This handles various connection related
190 events, but not packet processing. It's done in dedicated packet
191 processing FSM thread. */
193 SILC_FSM_STATE(silc_client_connection_st_run)
195 SilcClientConnection conn = fsm_context;
196 SilcFSMThread thread;
198 /* Wait for events */
199 SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
202 thread = &conn->internal->event_thread;
204 if (conn->internal->connect) {
205 SILC_LOG_DEBUG(("Event: connect"));
206 conn->internal->connect = FALSE;
208 /*** Event: connect */
209 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
211 silc_fsm_start_sync(thread, silc_client_st_connect);
212 return SILC_FSM_CONTINUE;
215 if (conn->internal->key_exchange) {
216 SILC_LOG_DEBUG(("Event: key exchange"));
217 conn->internal->key_exchange = FALSE;
219 /*** Event: key exchange */
220 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
222 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
223 return SILC_FSM_CONTINUE;
226 if (conn->internal->rekeying) {
227 SILC_LOG_DEBUG(("Event: rekey"));
228 conn->internal->rekeying = FALSE;
231 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
233 silc_fsm_start_sync(thread, silc_client_st_rekey);
234 return SILC_FSM_CONTINUE;
237 if (conn->internal->disconnected) {
238 /** Event: disconnected */
239 SILC_LOG_DEBUG(("Event: disconnected"));
240 conn->internal->disconnected = FALSE;
241 silc_fsm_next(fsm, silc_client_connection_st_close);
242 return SILC_FSM_CONTINUE;
247 return SILC_FSM_CONTINUE;
250 /* Packet processor thread. Each incoming packet is processed in FSM
251 thread in this state. The thread is run in the connection machine. */
253 SILC_FSM_STATE(silc_client_connection_st_packet)
255 SilcClientConnection conn = fsm_context;
256 SilcPacket packet = state_context;
258 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
260 switch (packet->type) {
262 case SILC_PACKET_PRIVATE_MESSAGE:
263 /** Private message */
264 silc_fsm_next(fsm, silc_client_private_message);
267 case SILC_PACKET_CHANNEL_MESSAGE:
268 /** Channel message */
269 silc_fsm_next(fsm, silc_client_channel_message);
272 case SILC_PACKET_FTP:
273 /* File transfer packet */
274 // silc_client_ftp(client, conn, packet);
277 case SILC_PACKET_CHANNEL_KEY:
279 silc_fsm_next(fsm, silc_client_channel_key);
282 case SILC_PACKET_COMMAND_REPLY:
284 silc_fsm_next(fsm, silc_client_command_reply);
287 case SILC_PACKET_NOTIFY:
289 silc_fsm_next(fsm, silc_client_notify);
292 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
293 /* Private message key indicator */
294 silc_fsm_next(fsm, silc_client_private_message_key);
297 case SILC_PACKET_DISCONNECT:
299 silc_fsm_next(fsm, silc_client_disconnect);
302 case SILC_PACKET_ERROR:
303 /* Error by server */
304 silc_fsm_next(fsm, silc_client_error);
307 case SILC_PACKET_KEY_AGREEMENT:
309 silc_fsm_next(fsm, silc_client_key_agreement);
312 case SILC_PACKET_COMMAND:
313 /** Command packet */
314 silc_fsm_next(fsm, silc_client_command);
317 case SILC_PACKET_NEW_ID:
319 silc_fsm_next(fsm, silc_client_new_id);
322 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
323 /* Reply to connection authentication request to resolve authentication
324 method from server. */
325 // silc_client_connection_auth_request(client, conn, packet);
328 case SILC_PACKET_REKEY:
329 /* Signal to start rekey */
330 conn->internal->rekey_responder = TRUE;
331 conn->internal->rekeying = TRUE;
332 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
334 silc_packet_free(packet);
335 return SILC_FSM_FINISH;
339 silc_packet_free(packet);
340 return SILC_FSM_FINISH;
344 return SILC_FSM_CONTINUE;
347 /* Disconnection even to close remote connection. We close the connection
348 and finish the connection machine in this state. The connection context
349 is deleted in the machine destructor. The connection callback must be
350 already called back to application before getting here. */
352 SILC_FSM_STATE(silc_client_connection_st_close)
354 SilcClientConnection conn = fsm_context;
356 SILC_LOG_DEBUG(("Closing remote connection"));
358 /* Abort ongoing events */
359 if (conn->internal->op)
360 silc_async_abort(conn->internal->op, NULL, NULL);
362 /* Close connection */
363 silc_packet_stream_destroy(conn->stream);
365 SILC_LOG_DEBUG(("Finishing connection machine"));
367 return SILC_FSM_FINISH;
370 /* Received error packet from server. Send it to application. */
372 SILC_FSM_STATE(silc_client_error)
374 SilcClientConnection conn = fsm_context;
375 SilcClient client = conn->client;
376 SilcPacket packet = state_context;
379 msg = silc_memdup(silc_buffer_data(&packet->buffer),
380 silc_buffer_len(&packet->buffer));
382 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
385 silc_packet_free(packet);
387 return SILC_FSM_FINISH;
390 /* Received disconnect packet from server. We close the connection and
391 send the disconnect message to application. */
393 SILC_FSM_STATE(silc_client_disconnect)
395 SilcClientConnection conn = fsm_context;
396 SilcClient client = conn->client;
397 SilcPacket packet = state_context;
399 char *message = NULL;
401 SILC_LOG_DEBUG(("Server disconnected"));
403 if (silc_buffer_len(&packet->buffer) < 1) {
404 silc_packet_free(packet);
405 return SILC_FSM_FINISH;
408 status = (SilcStatus)packet->buffer.data[0];
410 silc_buffer_pull(&packet->buffer, 1);
411 if (silc_buffer_len(&packet->buffer) > 1 &&
412 silc_utf8_valid(silc_buffer_data(&packet->buffer),
413 silc_buffer_len(&packet->buffer)))
414 message = silc_memdup(silc_buffer_data(&packet->buffer),
415 silc_buffer_len(&packet->buffer));
417 /* Call connection callback */
418 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
419 message, conn->callback_context);
422 silc_packet_free(packet);
424 /* Signal to close connection */
425 if (!conn->internal->disconnected) {
426 conn->internal->disconnected = TRUE;
427 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
430 return SILC_FSM_FINISH;
433 /*************************** Main client machine ****************************/
435 /* The client's main state where we wait for various events */
437 SILC_FSM_STATE(silc_client_st_run)
439 SilcClient client = fsm_context;
441 /* Wait for events */
442 SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
446 if (client->internal->run_callback && client->internal->ops->running) {
447 /* Call running callbcak back to application */
448 SILC_LOG_DEBUG(("We are running, call running callback"));
449 client->internal->run_callback = FALSE;
450 client->internal->ops->running(client, client->application);
451 return SILC_FSM_CONTINUE;
456 return SILC_FSM_CONTINUE;
459 /******************************* Private API ********************************/
461 /* Adds new connection. Creates the connection context and returns it. */
463 static SilcClientConnection
464 silc_client_add_connection(SilcClient client,
465 SilcConnectionType conn_type,
466 SilcClientConnectionParams *params,
467 SilcPublicKey public_key,
468 SilcPrivateKey private_key,
469 char *remote_host, int port,
470 SilcClientConnectCallback callback,
473 SilcClientConnection conn;
474 SilcFSMThread thread;
479 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
481 conn = silc_calloc(1, sizeof(*conn));
485 conn->client = client;
486 conn->public_key = public_key;
487 conn->private_key = private_key;
488 conn->remote_host = strdup(remote_host);
489 conn->remote_port = port ? port : 706;
490 conn->type = conn_type;
491 conn->callback = callback;
492 conn->callback_context = context;
494 conn->internal = silc_calloc(1, sizeof(*conn->internal));
495 if (!conn->internal) {
499 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
500 silc_mutex_alloc(&conn->internal->lock);
501 silc_atomic_init16(&conn->internal->cmd_ident, 0);
503 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
505 silc_free(conn->internal);
511 conn->internal->params = *params;
512 if (!conn->internal->params.rekey_secs)
513 conn->internal->params.rekey_secs = 3600;
514 #ifndef SILC_DIST_INPLACE
515 if (conn->internal->params.rekey_secs < 300)
516 conn->internal->params.rekey_secs = 300;
517 #endif /* SILC_DIST_INPLACE */
519 conn->internal->verbose = TRUE;
520 silc_list_init(conn->internal->pending_commands,
521 struct SilcClientCommandContextStruct, next);
522 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
524 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
526 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
528 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
530 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
531 !conn->internal->server_cache) {
532 silc_client_del_connection(client, conn);
536 conn->internal->ftp_sessions = silc_dlist_init();
538 /* Run the connection state machine. If threads are in use the machine
539 is always run in a real thread. */
540 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
541 silc_client_fsm_destructor, NULL,
542 client->internal->params->threads);
544 silc_client_del_connection(client, conn);
547 silc_fsm_start(thread, silc_client_connection_st_start);
549 SILC_LOG_DEBUG(("New connection %p", conn));
554 /* Deletes connection. This is always called from the connection machine
555 destructor. Do not call this directly other places. */
557 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
560 SilcIDCacheEntry entry;
561 SilcFSMThread thread;
562 SilcClientCommandContext cmd;
564 SILC_LOG_DEBUG(("Freeing connection %p", conn));
566 silc_schedule_task_del_by_context(conn->internal->schedule, conn);
568 /* Free all cache entries */
569 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
570 silc_list_start(list);
571 while ((entry = silc_list_get(list)))
572 silc_client_del_server(client, conn, entry->context);
574 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
575 silc_list_start(list);
576 while ((entry = silc_list_get(list))) {
577 silc_client_empty_channel(client, conn, entry->context);
578 silc_client_del_channel(client, conn, entry->context);
581 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
582 silc_list_start(list);
583 while ((entry = silc_list_get(list)))
584 silc_client_del_client(client, conn, entry->context);
588 if (conn->internal->client_cache)
589 silc_idcache_free(conn->internal->client_cache);
590 if (conn->internal->channel_cache)
591 silc_idcache_free(conn->internal->channel_cache);
592 if (conn->internal->server_cache)
593 silc_idcache_free(conn->internal->server_cache);
595 /* Free thread pool */
596 silc_list_start(conn->internal->thread_pool);
597 while ((thread = silc_list_get(conn->internal->thread_pool)))
598 silc_fsm_free(thread);
600 /* Free pending commands */
601 silc_list_start(conn->internal->pending_commands);
602 while ((cmd = silc_list_get(conn->internal->pending_commands)))
603 silc_client_command_free(cmd);
605 silc_free(conn->remote_host);
606 silc_buffer_free(conn->internal->local_idp);
607 silc_buffer_free(conn->internal->remote_idp);
608 silc_mutex_free(conn->internal->lock);
609 if (conn->internal->hash)
610 silc_hash_free(conn->internal->hash);
611 if (conn->internal->sha1hash)
612 silc_hash_free(conn->internal->sha1hash);
613 silc_atomic_uninit16(&conn->internal->cmd_ident);
615 silc_free(conn->internal);
616 memset(conn, 'F', sizeof(*conn));
621 /******************************* Client API *********************************/
623 /* Connects to remote server. This is the main routine used to connect
624 to remote SILC server. Performs key exchange also. Returns the
625 connection context to the connection callback. */
627 SilcBool silc_client_connect_to_server(SilcClient client,
628 SilcClientConnectionParams *params,
629 SilcPublicKey public_key,
630 SilcPrivateKey private_key,
631 char *remote_host, int port,
632 SilcClientConnectCallback callback,
635 SilcClientConnection conn;
637 if (!client || !remote_host)
640 /* Add new connection */
641 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
642 public_key, private_key, remote_host,
643 port, callback, context);
645 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
649 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
650 "Connecting to port %d of server %s",
653 /* Signal connection machine to start connecting */
654 conn->internal->connect = TRUE;
658 /* Connects to remote client. Performs key exchange also. Returns the
659 connection context to the connection callback. */
661 SilcBool silc_client_connect_to_client(SilcClient client,
662 SilcClientConnectionParams *params,
663 SilcPublicKey public_key,
664 SilcPrivateKey private_key,
665 char *remote_host, int port,
666 SilcClientConnectCallback callback,
669 SilcClientConnection conn;
671 if (!client || !remote_host)
674 /* Add new connection */
675 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
676 public_key, private_key, remote_host,
677 port, callback, context);
679 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
683 /* Signal connection machine to start connecting */
684 conn->internal->connect = TRUE;
688 /* Starts key exchange in the remote stream indicated by `stream'. This
689 creates the connection context and returns it in the connection callback. */
691 SilcBool silc_client_key_exchange(SilcClient client,
692 SilcClientConnectionParams *params,
693 SilcPublicKey public_key,
694 SilcPrivateKey private_key,
696 SilcConnectionType conn_type,
697 SilcClientConnectCallback callback,
700 SilcClientConnection conn;
704 if (!client || !stream)
707 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
708 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
709 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
713 /* Add new connection */
714 conn = silc_client_add_connection(client, conn_type, params,
715 public_key, private_key,
716 (char *)host, port, callback, context);
718 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
721 conn->stream = (void *)stream;
723 /* Signal connection to start key exchange */
724 conn->internal->key_exchange = TRUE;
728 /* Closes remote connection */
730 void silc_client_close_connection(SilcClient client,
731 SilcClientConnection conn)
733 SILC_LOG_DEBUG(("Closing connection %p", conn));
735 /* Signal to close connection */
736 if (!conn->internal->disconnected) {
737 conn->internal->disconnected = TRUE;
738 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
743 /* Finalizes the connection to the remote SILC server. This is called
744 after authentication protocol has been completed. This send our
745 user information to the server to receive our client ID from
748 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
750 SilcProtocol protocol = (SilcProtocol)context;
751 SilcClientConnAuthInternalContext *ctx =
752 (SilcClientConnAuthInternalContext *)protocol->context;
753 SilcClient client = (SilcClient)ctx->client;
754 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
757 SILC_LOG_DEBUG(("Start"));
759 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
760 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
761 /* Error occured during protocol */
762 SILC_LOG_DEBUG(("Error during authentication protocol"));
763 ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
767 if (conn->internal->params.detach_data) {
768 /* Send RESUME_CLIENT packet to the server, which is used to resume
769 old detached session back. */
771 SilcClientID *old_client_id;
772 unsigned char *old_id;
773 SilcUInt16 old_id_len;
775 if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
776 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
780 old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
781 if (!old_client_id) {
783 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
787 /* Generate authentication data that server will verify */
788 auth = silc_auth_public_key_auth_generate(client->public_key,
791 conn->internal->hash,
792 old_client_id, SILC_ID_CLIENT);
794 silc_free(old_client_id);
796 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
800 packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
801 silc_buffer_format(packet,
802 SILC_STR_UI_SHORT(old_id_len),
803 SILC_STR_UI_XNSTRING(old_id, old_id_len),
804 SILC_STR_UI_XNSTRING(auth->data, auth->len),
807 /* Send the packet */
808 silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
810 packet->data, packet->len, TRUE);
811 silc_buffer_free(packet);
812 silc_buffer_free(auth);
813 silc_free(old_client_id);
816 /* Send NEW_CLIENT packet to the server. We will become registered
817 to the SILC network after sending this packet and we will receive
818 client ID from the server. */
819 packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
820 strlen(client->realname));
821 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
822 silc_buffer_format(packet,
823 SILC_STR_UI_SHORT(strlen(client->username)),
824 SILC_STR_UI_XNSTRING(client->username,
825 strlen(client->username)),
826 SILC_STR_UI_SHORT(strlen(client->realname)),
827 SILC_STR_UI_XNSTRING(client->realname,
828 strlen(client->realname)),
831 /* Send the packet */
832 silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
834 packet->data, packet->len, TRUE);
835 silc_buffer_free(packet);
838 /* Save remote ID. */
839 conn->remote_id = ctx->dest_id;
840 conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
841 conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
843 /* Register re-key timeout */
844 conn->internal->rekey->timeout = client->internal->params->rekey_secs;
845 conn->internal->rekey->context = (void *)client;
846 silc_schedule_task_add(client->schedule, conn->sock->sock,
847 silc_client_rekey_callback,
848 (void *)conn->sock, conn->internal->rekey->timeout, 0,
849 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
851 silc_protocol_free(protocol);
852 silc_free(ctx->auth_data);
853 silc_socket_free(ctx->sock);
855 conn->sock->protocol = NULL;
859 silc_protocol_free(protocol);
860 silc_free(ctx->auth_data);
861 silc_free(ctx->dest_id);
862 conn->sock->protocol = NULL;
863 silc_socket_free(ctx->sock);
865 /* Notify application of failure */
866 silc_schedule_task_add(client->schedule, ctx->sock->sock,
867 silc_client_connect_failure_auth, ctx,
868 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
871 /* Client session resuming callback. If the session was resumed
872 this callback is called after the resuming is completed. This
873 will call the `connect' client operation to the application
874 since it has not been called yet. */
876 static void silc_client_resume_session_cb(SilcClient client,
877 SilcClientConnection conn,
883 /* Notify application that connection is created to server */
884 client->internal->ops->connected(client, conn, success ?
885 SILC_CLIENT_CONN_SUCCESS_RESUME :
886 SILC_CLIENT_CONN_ERROR_RESUME);
889 /* Issue INFO command to fetch the real server name and server
890 information and other stuff. */
891 silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
892 silc_client_command_reply_info_i, 0,
894 sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
895 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
896 conn->cmd_ident, 1, 2, sidp->data, sidp->len);
897 silc_buffer_free(sidp);
901 /* Processes incoming connection authentication method request packet.
902 It is a reply to our previously sent request. The packet can be used
903 to resolve the authentication method for the current session if the
904 client does not know it beforehand. */
906 void silc_client_connection_auth_request(SilcClient client,
907 SilcClientConnection conn,
908 SilcPacketContext *packet)
910 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
911 SilcUInt16 conn_type, auth_meth;
914 /* If we haven't send our request then ignore this one. */
915 if (!conn->internal->connauth)
918 /* Parse the payload */
919 ret = silc_buffer_unformat(packet->buffer,
920 SILC_STR_UI_SHORT(&conn_type),
921 SILC_STR_UI_SHORT(&auth_meth),
924 auth_meth = SILC_AUTH_NONE;
926 /* Call the request callback to notify application for received
927 authentication method information. */
928 if (conn->internal->connauth->callback)
929 (*conn->internal->connauth->callback)(client, conn, auth_meth,
930 conn->internal->connauth->context);
932 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
934 silc_free(conn->internal->connauth);
935 conn->internal->connauth = NULL;
938 /* Timeout task callback called if the server does not reply to our
939 connection authentication method request in the specified time interval. */
941 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
943 SilcClientConnection conn = (SilcClientConnection)context;
944 SilcClient client = conn->client;
946 if (!conn->internal->connauth)
949 /* Call the request callback to notify application */
950 if (conn->internal->connauth->callback)
951 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
952 conn->internal->connauth->context);
954 silc_free(conn->internal->connauth);
955 conn->internal->connauth = NULL;
958 /* This function can be used to request the current authentication method
959 from the server. This may be called when connecting to the server
960 and the client library requests the authentication data from the
961 application. If the application does not know the current authentication
962 method it can request it from the server using this function.
963 The `callback' with `context' will be called after the server has
964 replied back with the current authentication method. */
967 silc_client_request_authentication_method(SilcClient client,
968 SilcClientConnection conn,
969 SilcConnectionAuthRequest callback,
972 SilcClientConnAuthRequest connauth;
975 assert(client && conn);
976 connauth = silc_calloc(1, sizeof(*connauth));
977 connauth->callback = callback;
978 connauth->context = context;
980 if (conn->internal->connauth)
981 silc_free(conn->internal->connauth);
983 conn->internal->connauth = connauth;
985 /* Assemble the request packet and send it to the server */
986 packet = silc_buffer_alloc(4);
987 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
988 silc_buffer_format(packet,
989 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
990 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
992 silc_client_packet_send(client, conn->sock,
993 SILC_PACKET_CONNECTION_AUTH_REQUEST,
995 packet->data, packet->len, FALSE);
996 silc_buffer_free(packet);
998 /* Register a timeout in case server does not reply anything back. */
1000 silc_schedule_task_add(client->schedule, conn->sock->sock,
1001 silc_client_request_authentication_method_timeout,
1003 client->internal->params->connauth_request_secs, 0,
1004 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1009 /* Allocates new client object. This has to be done before client may
1010 work. After calling this one must call silc_client_init to initialize
1011 the client. The `application' is application specific user data pointer
1012 and caller must free it. */
1014 SilcClient silc_client_alloc(SilcClientOperations *ops,
1015 SilcClientParams *params,
1017 const char *version_string)
1019 SilcClient new_client;
1021 new_client = silc_calloc(1, sizeof(*new_client));
1024 new_client->application = application;
1026 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
1027 if (!new_client->internal) {
1028 silc_free(new_client);
1031 new_client->internal->ops = ops;
1032 new_client->internal->params =
1033 silc_calloc(1, sizeof(*new_client->internal->params));
1034 if (!version_string)
1035 version_string = silc_version_string;
1036 new_client->internal->silc_client_version = strdup(version_string);
1039 memcpy(new_client->internal->params, params, sizeof(*params));
1041 if (!new_client->internal->params->connauth_request_secs)
1042 new_client->internal->params->connauth_request_secs = 2;
1044 new_client->internal->params->
1045 nickname_format[sizeof(new_client->internal->
1046 params->nickname_format) - 1] = 0;
1051 /* Frees client object and its internals. */
1053 void silc_client_free(SilcClient client)
1056 silc_rng_free(client->rng);
1058 if (!client->internal->params->dont_register_crypto_library) {
1059 silc_cipher_unregister_all();
1060 silc_pkcs_unregister_all();
1061 silc_hash_unregister_all();
1062 silc_hmac_unregister_all();
1065 silc_free(client->username);
1066 silc_free(client->hostname);
1067 silc_free(client->realname);
1068 silc_free(client->internal->params);
1069 silc_free(client->internal->silc_client_version);
1070 silc_free(client->internal);
1074 /* Initializes the client. This makes all the necessary steps to make
1075 the client ready to be run. One must call silc_client_run to run the
1076 client. Returns FALSE if error occured, TRUE otherwise. */
1078 SilcBool silc_client_init(SilcClient client, const char *username,
1079 const char *hostname, const char *realname)
1081 SILC_LOG_DEBUG(("Initializing client"));
1086 if (!username || !hostname) {
1087 SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1088 "silc_client_init"));
1092 realname = username;
1094 /* Validate essential strings */
1095 if (!silc_identifier_verify(username, strlen(username),
1096 SILC_STRING_UTF8, 128)) {
1097 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1101 if (!silc_identifier_verify(hostname, strlen(hostname),
1102 SILC_STRING_UTF8, 256)) {
1103 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1107 if (!silc_utf8_valid(realname, strlen(realname))) {
1108 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1113 /* Take the name strings */
1114 client->username = strdup(username);
1115 client->hostname = strdup(hostname);
1116 client->realname = strdup(realname);
1117 if (!username || !hostname || !realname)
1120 if (!client->internal->params->dont_register_crypto_library) {
1121 /* Initialize the crypto library. If application has done this already
1122 this has no effect. Also, we will not be overriding something
1123 application might have registered earlier. */
1124 silc_cipher_register_default();
1125 silc_pkcs_register_default();
1126 silc_hash_register_default();
1127 silc_hmac_register_default();
1130 /* Initialize random number generator */
1131 client->rng = silc_rng_alloc();
1134 silc_rng_init(client->rng);
1135 silc_rng_global_init(client->rng);
1137 /* Initialize the scheduler */
1138 client->schedule = silc_schedule_init(0, client);
1139 if (!client->schedule)
1142 /* Start packet engine */
1143 client->internal->packet_engine =
1144 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1146 if (!client->internal->packet_engine)
1149 /* Allocate client lock */
1150 silc_mutex_alloc(&client->internal->lock);
1152 /* Register commands */
1153 silc_client_commands_register(client);
1155 /* Initialize and start the client FSM */
1156 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1157 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1158 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1160 /* Signal the application when we are running */
1161 client->internal->run_callback = TRUE;
1162 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1167 /* Stops the client. This is called to stop the client and thus to stop
1170 void silc_client_stop(SilcClient client)
1172 SILC_LOG_DEBUG(("Stopping client"));
1174 silc_schedule_stop(client->schedule);
1175 silc_schedule_uninit(client->schedule);
1176 silc_client_commands_unregister(client);
1178 SILC_LOG_DEBUG(("Client stopped"));
1181 /* Starts the SILC client FSM machine and blocks here. When this returns
1182 the client has ended. */
1184 void silc_client_run(SilcClient client)
1186 SILC_LOG_DEBUG(("Starting SILC client"));
1188 /* Run the scheduler */
1189 silc_schedule(client->schedule);
1192 /* Call scheduler one iteration and return. This cannot be called if threads
1195 void silc_client_run_one(SilcClient client)
1197 silc_schedule_one(client->schedule, 0);