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:
82 case SILC_PACKET_REKEY_DONE:
83 case SILC_PACKET_CONNECTION_AUTH:
84 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
89 /* Get packet processing thread */
90 thread = silc_list_get(conn->internal->thread_pool);
92 thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
93 silc_client_packet_destructor, NULL, FALSE);
97 silc_list_del(conn->internal->thread_pool, thread);
98 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
99 silc_client_packet_destructor, NULL, FALSE);
102 /* Process packet in thread */
103 silc_fsm_set_state_context(thread, packet);
104 silc_fsm_start_sync(thread, silc_client_connection_st_packet);
109 /* Packet engine callback to indicate end of stream */
111 static void silc_client_packet_eos(SilcPacketEngine engine,
112 SilcPacketStream stream,
113 void *callback_context,
114 void *stream_context)
116 SILC_LOG_DEBUG(("End of stream received"));
119 /* Packet engine callback to indicate error */
121 static void silc_client_packet_error(SilcPacketEngine engine,
122 SilcPacketStream stream,
123 SilcPacketError error,
124 void *callback_context,
125 void *stream_context)
130 /* Packet stream callbacks */
131 static SilcPacketCallbacks silc_client_stream_cbs =
133 silc_client_packet_receive,
134 silc_client_packet_eos,
135 silc_client_packet_error
140 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
141 void *destructor_context)
147 /************************** Connection's machine ****************************/
149 /* Start the connection's state machine. If threads are in use the machine
150 is always executed in a real thread. */
152 SILC_FSM_STATE(silc_client_connection_st_start)
154 SilcClientConnection conn = fsm_context;
157 /* Take scheduler for connection */
158 conn->internal->schedule = silc_fsm_get_schedule(fsm);
160 /*** Run connection machine */
161 connfsm = &conn->internal->fsm;
162 silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
163 fsm, conn->internal->schedule);
164 silc_fsm_sema_init(&conn->internal->wait_event, connfsm, 0);
165 silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
167 /* Schedule any events set in initialization */
168 if (conn->internal->connect)
169 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
170 if (conn->internal->key_exchange)
171 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
173 /* Wait until this thread is terminated from the machine destructor */
174 return SILC_FSM_WAIT;
177 /* Connection machine main state. This handles various connection related
178 events, but not packet processing. It's done in dedicated packet
179 processing FSM thread. */
181 SILC_FSM_STATE(silc_client_connection_st_run)
183 SilcClientConnection conn = fsm_context;
184 SilcFSMThread thread;
186 /* Wait for events */
187 SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
190 thread = &conn->internal->event_thread;
192 if (conn->internal->connect) {
193 SILC_LOG_DEBUG(("Event: connect"));
194 conn->internal->connect = FALSE;
196 /*** Event: connect */
197 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
199 silc_fsm_start_sync(thread, silc_client_st_connect);
200 return SILC_FSM_CONTINUE;
203 if (conn->internal->key_exchange) {
204 SILC_LOG_DEBUG(("Event: key exchange"));
205 conn->internal->key_exchange = FALSE;
207 /*** Event: key exchange */
208 silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
210 silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
211 return SILC_FSM_CONTINUE;
214 if (conn->internal->disconnected) {
215 /** Event: disconnected */
216 SILC_LOG_DEBUG(("Event: disconnected"));
217 conn->internal->disconnected = FALSE;
218 silc_fsm_next(fsm, silc_client_connection_st_close);
219 return SILC_FSM_CONTINUE;
224 return SILC_FSM_CONTINUE;
227 /* Packet processor thread. Each incoming packet is processed in FSM
228 thread in this state. The thread is run in the connection machine. */
230 SILC_FSM_STATE(silc_client_connection_st_packet)
232 SilcPacket packet = state_context;
234 SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
236 switch (packet->type) {
238 case SILC_PACKET_PRIVATE_MESSAGE:
239 /** Private message */
240 silc_fsm_next(fsm, silc_client_private_message);
243 case SILC_PACKET_CHANNEL_MESSAGE:
244 /** Channel message */
245 silc_fsm_next(fsm, silc_client_channel_message);
248 case SILC_PACKET_FTP:
249 /* File transfer packet */
250 // silc_client_ftp(client, conn, packet);
253 case SILC_PACKET_CHANNEL_KEY:
255 silc_fsm_next(fsm, silc_client_channel_key);
258 case SILC_PACKET_COMMAND_REPLY:
260 silc_fsm_next(fsm, silc_client_command_reply);
263 case SILC_PACKET_NOTIFY:
265 silc_fsm_next(fsm, silc_client_notify);
268 case SILC_PACKET_PRIVATE_MESSAGE_KEY:
269 /* Private message key indicator */
270 silc_fsm_next(fsm, silc_client_private_message_key);
273 case SILC_PACKET_DISCONNECT:
275 silc_fsm_next(fsm, silc_client_disconnect);
278 case SILC_PACKET_ERROR:
279 /* Error by server */
280 silc_fsm_next(fsm, silc_client_error);
283 case SILC_PACKET_KEY_AGREEMENT:
285 // silc_client_key_agreement(client, conn, packet);
288 case SILC_PACKET_COMMAND:
289 /** Command packet */
290 silc_fsm_next(fsm, silc_client_command);
293 case SILC_PACKET_NEW_ID:
295 silc_fsm_next(fsm, silc_client_new_id);
297 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
298 /* Reply to connection authentication request to resolve authentication
299 method from server. */
300 // silc_client_connection_auth_request(client, conn, packet);
304 silc_packet_free(packet);
305 return SILC_FSM_FINISH;
309 return SILC_FSM_CONTINUE;
312 /* Disconnection even to close remote connection. We close the connection
313 and finish the connection machine in this state. The connection context
314 is deleted in the machine destructor. The connection callback must be
315 already called back to application before getting here. */
317 SILC_FSM_STATE(silc_client_connection_st_close)
319 SilcClientConnection conn = fsm_context;
321 SILC_LOG_DEBUG(("Closing remote connection"));
323 /* XXX abort any ongoing events (protocols) */
325 /* Close connection */
326 silc_packet_stream_destroy(conn->stream);
328 SILC_LOG_DEBUG(("Finishing connection machine"));
330 return SILC_FSM_FINISH;
333 /* Received error packet from server. Send it to application. */
335 SILC_FSM_STATE(silc_client_error)
337 SilcClientConnection conn = fsm_context;
338 SilcClient client = conn->client;
339 SilcPacket packet = state_context;
342 msg = silc_memdup(silc_buffer_data(&packet->buffer),
343 silc_buffer_len(&packet->buffer));
345 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
348 silc_packet_free(packet);
350 return SILC_FSM_FINISH;
353 /* Received disconnect packet from server. We close the connection and
354 send the disconnect message to application. */
356 SILC_FSM_STATE(silc_client_disconnect)
358 SilcClientConnection conn = fsm_context;
359 SilcClient client = conn->client;
360 SilcPacket packet = state_context;
362 char *message = NULL;
364 SILC_LOG_DEBUG(("Server disconnected"));
366 if (silc_buffer_len(&packet->buffer) < 1) {
367 silc_packet_free(packet);
368 return SILC_FSM_FINISH;
371 status = (SilcStatus)packet->buffer.data[0];
373 silc_buffer_pull(&packet->buffer, 1);
374 if (silc_buffer_len(&packet->buffer) > 1 &&
375 silc_utf8_valid(silc_buffer_data(&packet->buffer),
376 silc_buffer_len(&packet->buffer)))
377 message = silc_memdup(silc_buffer_data(&packet->buffer),
378 silc_buffer_len(&packet->buffer));
380 /* Call connection callback */
381 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
382 message, conn->callback_context);
385 silc_packet_free(packet);
387 /* Signal to close connection */
388 conn->internal->disconnected = TRUE;
389 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
391 return SILC_FSM_FINISH;
394 /*************************** Main client machine ****************************/
396 /* The client's main state where we wait for various events */
398 SILC_FSM_STATE(silc_client_st_run)
400 SilcClient client = fsm_context;
402 /* Wait for events */
403 SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
407 if (client->internal->run_callback && client->internal->ops->running) {
408 /* Call running callbcak back to application */
409 SILC_LOG_DEBUG(("We are running, call running callback"));
410 client->internal->run_callback = FALSE;
411 client->internal->ops->running(client, client->application);
412 return SILC_FSM_CONTINUE;
417 return SILC_FSM_CONTINUE;
420 /******************************* Private API ********************************/
422 /* Adds new connection. Creates the connection context and returns it. */
424 static SilcClientConnection
425 silc_client_add_connection(SilcClient client,
426 SilcConnectionType conn_type,
427 SilcClientConnectionParams *params,
428 SilcPublicKey public_key,
429 SilcPrivateKey private_key,
430 char *remote_host, int port,
431 SilcClientConnectCallback callback,
434 SilcClientConnection conn;
435 SilcFSMThread thread;
440 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
442 conn = silc_calloc(1, sizeof(*conn));
446 conn->client = client;
447 conn->public_key = public_key;
448 conn->private_key = private_key;
449 conn->remote_host = strdup(remote_host);
450 conn->remote_port = port ? port : 706;
451 conn->type = conn_type;
452 conn->callback = callback;
453 conn->callback_context = context;
455 conn->internal = silc_calloc(1, sizeof(*conn->internal));
456 if (!conn->internal) {
461 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
463 silc_free(conn->internal);
467 conn->internal->params = *params;
468 conn->internal->verbose = TRUE;
469 silc_list_init(conn->internal->pending_commands,
470 struct SilcClientCommandContextStruct, next);
471 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
473 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
475 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
477 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
479 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
480 !conn->internal->server_cache) {
481 silc_client_del_connection(client, conn);
485 conn->internal->ftp_sessions = silc_dlist_init();
487 /* Run the connection state machine. If threads are in use the machine
488 is always run in a real thread. */
489 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
490 silc_client_fsm_destructor, NULL,
491 client->internal->params->threads);
493 silc_client_del_connection(client, conn);
496 silc_fsm_start(thread, silc_client_connection_st_start);
501 /* Removes connection from client. Frees all memory. */
503 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
506 SilcClientConnection c;
507 SilcIDCacheList list;
508 SilcIDCacheEntry entry;
509 SilcClientCommandPending *r;
512 silc_dlist_start(client->internal->conns);
513 while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
517 /* Free all cache entries */
518 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
519 ret = silc_idcache_list_first(list, &entry);
521 silc_client_del_client(client, conn, entry->context);
522 ret = silc_idcache_list_next(list, &entry);
524 silc_idcache_list_free(list);
527 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
528 ret = silc_idcache_list_first(list, &entry);
530 silc_client_del_channel(client, conn, entry->context);
531 ret = silc_idcache_list_next(list, &entry);
533 silc_idcache_list_free(list);
536 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
537 ret = silc_idcache_list_first(list, &entry);
539 silc_client_del_server(client, conn, entry->context);
540 ret = silc_idcache_list_next(list, &entry);
542 silc_idcache_list_free(list);
545 /* Clear ID caches */
546 if (conn->internal->client_cache)
547 silc_idcache_free(conn->internal->client_cache);
548 if (conn->internal->channel_cache)
549 silc_idcache_free(conn->internal->channel_cache);
550 if (conn->internal->server_cache)
551 silc_idcache_free(conn->internal->server_cache);
553 /* Free data (my ID is freed in above silc_client_del_client).
554 conn->nickname is freed when freeing the local_entry->nickname. */
555 silc_free(conn->remote_host);
556 silc_free(conn->local_id_data);
557 if (conn->internal->send_key)
558 silc_cipher_free(conn->internal->send_key);
559 if (conn->internal->receive_key)
560 silc_cipher_free(conn->internal->receive_key);
561 if (conn->internal->hmac_send)
562 silc_hmac_free(conn->internal->hmac_send);
563 if (conn->internal->hmac_receive)
564 silc_hmac_free(conn->internal->hmac_receive);
565 silc_free(conn->internal->rekey);
567 if (conn->internal->active_session) {
569 conn->sock->user_data = NULL;
570 silc_client_ftp_session_free(conn->internal->active_session);
571 conn->internal->active_session = NULL;
574 silc_client_ftp_free_sessions(client, conn);
576 if (conn->internal->pending_commands) {
577 silc_dlist_start(conn->internal->pending_commands);
578 while ((r = silc_dlist_get(conn->internal->pending_commands))
580 silc_dlist_del(conn->internal->pending_commands, r);
581 silc_dlist_uninit(conn->internal->pending_commands);
584 silc_free(conn->internal);
585 memset(conn, 0, sizeof(*conn));
588 silc_dlist_del(client->internal->conns, conn);
594 /******************************* Client API *********************************/
596 /* Connects to remote server. This is the main routine used to connect
597 to remote SILC server. Performs key exchange also. Returns the
598 connection context to the connection callback. */
600 SilcBool silc_client_connect_to_server(SilcClient client,
601 SilcClientConnectionParams *params,
602 SilcPublicKey public_key,
603 SilcPrivateKey private_key,
604 char *remote_host, int port,
605 SilcClientConnectCallback callback,
608 SilcClientConnection conn;
610 if (!client || !remote_host)
613 /* Add new connection */
614 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
615 public_key, private_key, remote_host,
616 port, callback, context);
618 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
622 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
623 "Connecting to port %d of server %s",
626 /* Signal connection machine to start connecting */
627 conn->internal->connect = TRUE;
631 /* Connects to remote client. Performs key exchange also. Returns the
632 connection context to the connection callback. */
634 SilcBool silc_client_connect_to_client(SilcClient client,
635 SilcClientConnectionParams *params,
636 SilcPublicKey public_key,
637 SilcPrivateKey private_key,
638 char *remote_host, int port,
639 SilcClientConnectCallback callback,
642 SilcClientConnection conn;
644 if (!client || !remote_host)
647 /* Add new connection */
648 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
649 public_key, private_key, remote_host,
650 port, callback, context);
652 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
656 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
657 "Connecting to port %d of client host %s",
660 /* Signal connection machine to start connecting */
661 conn->internal->connect = TRUE;
665 /* Starts key exchange in the remote stream indicated by `stream'. This
666 creates the connection context and returns it in the connection callback. */
668 SilcBool silc_client_key_exchange(SilcClient client,
669 SilcClientConnectionParams *params,
670 SilcPublicKey public_key,
671 SilcPrivateKey private_key,
673 SilcConnectionType conn_type,
674 SilcClientConnectCallback callback,
677 SilcClientConnection conn;
681 if (!client || !stream)
684 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port))
687 /* Add new connection */
688 conn = silc_client_add_connection(client, conn_type, params,
689 public_key, private_key,
690 (char *)host, port, callback, context);
692 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
695 conn->stream = (void *)stream;
697 /* Signal connection to start key exchange */
698 conn->internal->key_exchange = TRUE;
702 /* Closes remote connection */
704 void silc_client_close_connection(SilcClient client,
705 SilcClientConnection conn)
711 /* Finalizes the connection to the remote SILC server. This is called
712 after authentication protocol has been completed. This send our
713 user information to the server to receive our client ID from
716 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
718 SilcProtocol protocol = (SilcProtocol)context;
719 SilcClientConnAuthInternalContext *ctx =
720 (SilcClientConnAuthInternalContext *)protocol->context;
721 SilcClient client = (SilcClient)ctx->client;
722 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
725 SILC_LOG_DEBUG(("Start"));
727 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
728 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
729 /* Error occured during protocol */
730 SILC_LOG_DEBUG(("Error during authentication protocol"));
731 ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
735 if (conn->internal->params.detach_data) {
736 /* Send RESUME_CLIENT packet to the server, which is used to resume
737 old detached session back. */
739 SilcClientID *old_client_id;
740 unsigned char *old_id;
741 SilcUInt16 old_id_len;
743 if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
744 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
748 old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
749 if (!old_client_id) {
751 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
755 /* Generate authentication data that server will verify */
756 auth = silc_auth_public_key_auth_generate(client->public_key,
759 conn->internal->hash,
760 old_client_id, SILC_ID_CLIENT);
762 silc_free(old_client_id);
764 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
768 packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
769 silc_buffer_format(packet,
770 SILC_STR_UI_SHORT(old_id_len),
771 SILC_STR_UI_XNSTRING(old_id, old_id_len),
772 SILC_STR_UI_XNSTRING(auth->data, auth->len),
775 /* Send the packet */
776 silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
778 packet->data, packet->len, TRUE);
779 silc_buffer_free(packet);
780 silc_buffer_free(auth);
781 silc_free(old_client_id);
784 /* Send NEW_CLIENT packet to the server. We will become registered
785 to the SILC network after sending this packet and we will receive
786 client ID from the server. */
787 packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
788 strlen(client->realname));
789 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
790 silc_buffer_format(packet,
791 SILC_STR_UI_SHORT(strlen(client->username)),
792 SILC_STR_UI_XNSTRING(client->username,
793 strlen(client->username)),
794 SILC_STR_UI_SHORT(strlen(client->realname)),
795 SILC_STR_UI_XNSTRING(client->realname,
796 strlen(client->realname)),
799 /* Send the packet */
800 silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
802 packet->data, packet->len, TRUE);
803 silc_buffer_free(packet);
806 /* Save remote ID. */
807 conn->remote_id = ctx->dest_id;
808 conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
809 conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
811 /* Register re-key timeout */
812 conn->internal->rekey->timeout = client->internal->params->rekey_secs;
813 conn->internal->rekey->context = (void *)client;
814 silc_schedule_task_add(client->schedule, conn->sock->sock,
815 silc_client_rekey_callback,
816 (void *)conn->sock, conn->internal->rekey->timeout, 0,
817 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
819 silc_protocol_free(protocol);
820 silc_free(ctx->auth_data);
822 silc_ske_free(ctx->ske);
823 silc_socket_free(ctx->sock);
825 conn->sock->protocol = NULL;
829 silc_protocol_free(protocol);
830 silc_free(ctx->auth_data);
831 silc_free(ctx->dest_id);
833 silc_ske_free(ctx->ske);
834 conn->sock->protocol = NULL;
835 silc_socket_free(ctx->sock);
837 /* Notify application of failure */
838 silc_schedule_task_add(client->schedule, ctx->sock->sock,
839 silc_client_connect_failure_auth, ctx,
840 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
843 /* Client session resuming callback. If the session was resumed
844 this callback is called after the resuming is completed. This
845 will call the `connect' client operation to the application
846 since it has not been called yet. */
848 static void silc_client_resume_session_cb(SilcClient client,
849 SilcClientConnection conn,
855 /* Notify application that connection is created to server */
856 client->internal->ops->connected(client, conn, success ?
857 SILC_CLIENT_CONN_SUCCESS_RESUME :
858 SILC_CLIENT_CONN_ERROR_RESUME);
861 /* Issue INFO command to fetch the real server name and server
862 information and other stuff. */
863 silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
864 silc_client_command_reply_info_i, 0,
866 sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
867 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
868 conn->cmd_ident, 1, 2, sidp->data, sidp->len);
869 silc_buffer_free(sidp);
873 /* Processes incoming connection authentication method request packet.
874 It is a reply to our previously sent request. The packet can be used
875 to resolve the authentication method for the current session if the
876 client does not know it beforehand. */
878 void silc_client_connection_auth_request(SilcClient client,
879 SilcClientConnection conn,
880 SilcPacketContext *packet)
882 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
883 SilcUInt16 conn_type, auth_meth;
886 /* If we haven't send our request then ignore this one. */
887 if (!conn->internal->connauth)
890 /* Parse the payload */
891 ret = silc_buffer_unformat(packet->buffer,
892 SILC_STR_UI_SHORT(&conn_type),
893 SILC_STR_UI_SHORT(&auth_meth),
896 auth_meth = SILC_AUTH_NONE;
898 /* Call the request callback to notify application for received
899 authentication method information. */
900 if (conn->internal->connauth->callback)
901 (*conn->internal->connauth->callback)(client, conn, auth_meth,
902 conn->internal->connauth->context);
904 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
906 silc_free(conn->internal->connauth);
907 conn->internal->connauth = NULL;
910 /* Timeout task callback called if the server does not reply to our
911 connection authentication method request in the specified time interval. */
913 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
915 SilcClientConnection conn = (SilcClientConnection)context;
916 SilcClient client = conn->client;
918 if (!conn->internal->connauth)
921 /* Call the request callback to notify application */
922 if (conn->internal->connauth->callback)
923 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
924 conn->internal->connauth->context);
926 silc_free(conn->internal->connauth);
927 conn->internal->connauth = NULL;
930 /* This function can be used to request the current authentication method
931 from the server. This may be called when connecting to the server
932 and the client library requests the authentication data from the
933 application. If the application does not know the current authentication
934 method it can request it from the server using this function.
935 The `callback' with `context' will be called after the server has
936 replied back with the current authentication method. */
939 silc_client_request_authentication_method(SilcClient client,
940 SilcClientConnection conn,
941 SilcConnectionAuthRequest callback,
944 SilcClientConnAuthRequest connauth;
947 assert(client && conn);
948 connauth = silc_calloc(1, sizeof(*connauth));
949 connauth->callback = callback;
950 connauth->context = context;
952 if (conn->internal->connauth)
953 silc_free(conn->internal->connauth);
955 conn->internal->connauth = connauth;
957 /* Assemble the request packet and send it to the server */
958 packet = silc_buffer_alloc(4);
959 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
960 silc_buffer_format(packet,
961 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
962 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
964 silc_client_packet_send(client, conn->sock,
965 SILC_PACKET_CONNECTION_AUTH_REQUEST,
967 packet->data, packet->len, FALSE);
968 silc_buffer_free(packet);
970 /* Register a timeout in case server does not reply anything back. */
972 silc_schedule_task_add(client->schedule, conn->sock->sock,
973 silc_client_request_authentication_method_timeout,
975 client->internal->params->connauth_request_secs, 0,
976 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
981 /* Allocates new client object. This has to be done before client may
982 work. After calling this one must call silc_client_init to initialize
983 the client. The `application' is application specific user data pointer
984 and caller must free it. */
986 SilcClient silc_client_alloc(SilcClientOperations *ops,
987 SilcClientParams *params,
989 const char *version_string)
991 SilcClient new_client;
993 new_client = silc_calloc(1, sizeof(*new_client));
996 new_client->application = application;
998 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
999 if (!new_client->internal) {
1000 silc_free(new_client);
1003 new_client->internal->ops = ops;
1004 new_client->internal->params =
1005 silc_calloc(1, sizeof(*new_client->internal->params));
1006 if (!version_string)
1007 version_string = silc_version_string;
1008 new_client->internal->silc_client_version = strdup(version_string);
1011 memcpy(new_client->internal->params, params, sizeof(*params));
1013 if (!new_client->internal->params->task_max)
1014 new_client->internal->params->task_max = 200;
1016 if (!new_client->internal->params->rekey_secs)
1017 new_client->internal->params->rekey_secs = 3600;
1019 if (!new_client->internal->params->connauth_request_secs)
1020 new_client->internal->params->connauth_request_secs = 2;
1022 new_client->internal->params->
1023 nickname_format[sizeof(new_client->internal->
1024 params->nickname_format) - 1] = 0;
1029 /* Frees client object and its internals. */
1031 void silc_client_free(SilcClient client)
1035 silc_rng_free(client->rng);
1037 if (!client->internal->params->dont_register_crypto_library) {
1038 silc_cipher_unregister_all();
1039 silc_pkcs_unregister_all();
1040 silc_hash_unregister_all();
1041 silc_hmac_unregister_all();
1044 silc_free(client->internal->params);
1045 silc_free(client->internal->silc_client_version);
1046 silc_free(client->internal);
1051 /* Initializes the client. This makes all the necessary steps to make
1052 the client ready to be run. One must call silc_client_run to run the
1053 client. Returns FALSE if error occured, TRUE otherwise. */
1055 SilcBool silc_client_init(SilcClient client, const char *username,
1056 const char *hostname, const char *realname)
1058 SILC_LOG_DEBUG(("Initializing client"));
1063 if (!username || !hostname || !realname) {
1064 SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1065 "silc_client_init"));
1069 /* Validate essential strings */
1070 if (!silc_identifier_verify(username, strlen(username),
1071 SILC_STRING_UTF8, 128)) {
1072 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1076 if (!silc_identifier_verify(hostname, strlen(hostname),
1077 SILC_STRING_UTF8, 256)) {
1078 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1082 if (!silc_utf8_valid(realname, strlen(realname))) {
1083 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1088 /* Take the name strings */
1089 client->username = strdup(username);
1090 client->hostname = strdup(hostname);
1091 client->realname = strdup(realname);
1092 if (!username || !hostname || !realname)
1095 if (!client->internal->params->dont_register_crypto_library) {
1096 /* Initialize the crypto library. If application has done this already
1097 this has no effect. Also, we will not be overriding something
1098 application might have registered earlier. */
1099 silc_cipher_register_default();
1100 silc_pkcs_register_default();
1101 silc_hash_register_default();
1102 silc_hmac_register_default();
1105 /* Initialize random number generator */
1106 client->rng = silc_rng_alloc();
1107 silc_rng_init(client->rng);
1108 silc_rng_global_init(client->rng);
1110 /* Initialize the scheduler */
1112 silc_schedule_init(client->internal->params->task_max ?
1113 client->internal->params->task_max : 0, client);
1114 if (!client->schedule)
1117 /* Start packet engine */
1118 client->internal->packet_engine =
1119 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1121 if (!client->internal->packet_engine)
1124 /* Initialize FSM */
1125 if (!silc_fsm_init(&client->internal->fsm, client, NULL, NULL,
1128 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1130 /* Allocate client lock */
1131 silc_mutex_alloc(&client->internal->lock);
1133 /* Register commands */
1134 silc_client_commands_register(client);
1136 /* Start the client machine */
1137 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1139 /* Signal the application when we are running */
1140 client->internal->run_callback = TRUE;
1141 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1146 /* Stops the client. This is called to stop the client and thus to stop
1149 void silc_client_stop(SilcClient client)
1151 SILC_LOG_DEBUG(("Stopping client"));
1153 silc_schedule_stop(client->schedule);
1154 silc_schedule_uninit(client->schedule);
1155 silc_client_commands_unregister(client);
1157 SILC_LOG_DEBUG(("Client stopped"));
1160 /* Starts the SILC client FSM machine and blocks here. When this returns
1161 the client has ended. */
1163 void silc_client_run(SilcClient client)
1165 SILC_LOG_DEBUG(("Starting SILC client"));
1167 /* Run the scheduler */
1168 silc_schedule(client->schedule);
1171 /* Call scheduler one iteration and return. This cannot be called if threads
1174 void silc_client_run_one(SilcClient client)
1176 silc_schedule_one(client->schedule, 0);