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 client->internal->run_callback = FALSE;
410 client->internal->ops->running(client, client->application);
411 return SILC_FSM_CONTINUE;
416 return SILC_FSM_CONTINUE;
419 /******************************* Private API ********************************/
421 /* Adds new connection. Creates the connection context and returns it. */
423 static SilcClientConnection
424 silc_client_add_connection(SilcClient client,
425 SilcConnectionType conn_type,
426 SilcClientConnectionParams *params,
427 SilcPublicKey public_key,
428 SilcPrivateKey private_key,
429 char *remote_host, int port,
430 SilcClientConnectCallback callback,
433 SilcClientConnection conn;
434 SilcFSMThread thread;
439 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
441 conn = silc_calloc(1, sizeof(*conn));
445 conn->client = client;
446 conn->public_key = public_key;
447 conn->private_key = private_key;
448 conn->remote_host = strdup(remote_host);
449 conn->remote_port = port ? port : 706;
450 conn->type = conn_type;
451 conn->callback = callback;
452 conn->callback_context = context;
454 conn->internal = silc_calloc(1, sizeof(*conn->internal));
455 if (!conn->internal) {
460 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
462 silc_free(conn->internal);
466 conn->internal->params = *params;
467 conn->internal->verbose = TRUE;
468 silc_list_init(conn->internal->pending_commands,
469 struct SilcClientCommandContextStruct, next);
470 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
472 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
474 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
476 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
478 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
479 !conn->internal->server_cache) {
480 silc_client_del_connection(client, conn);
484 conn->internal->ftp_sessions = silc_dlist_init();
486 /* Run the connection state machine. If threads are in use the machine
487 is always run in a real thread. */
488 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
489 silc_client_fsm_destructor, NULL,
490 client->internal->params->threads);
492 silc_client_del_connection(client, conn);
495 silc_fsm_start(thread, silc_client_connection_st_start);
500 /* Removes connection from client. Frees all memory. */
502 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
505 SilcClientConnection c;
506 SilcIDCacheList list;
507 SilcIDCacheEntry entry;
508 SilcClientCommandPending *r;
511 silc_dlist_start(client->internal->conns);
512 while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
516 /* Free all cache entries */
517 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
518 ret = silc_idcache_list_first(list, &entry);
520 silc_client_del_client(client, conn, entry->context);
521 ret = silc_idcache_list_next(list, &entry);
523 silc_idcache_list_free(list);
526 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
527 ret = silc_idcache_list_first(list, &entry);
529 silc_client_del_channel(client, conn, entry->context);
530 ret = silc_idcache_list_next(list, &entry);
532 silc_idcache_list_free(list);
535 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
536 ret = silc_idcache_list_first(list, &entry);
538 silc_client_del_server(client, conn, entry->context);
539 ret = silc_idcache_list_next(list, &entry);
541 silc_idcache_list_free(list);
544 /* Clear ID caches */
545 if (conn->internal->client_cache)
546 silc_idcache_free(conn->internal->client_cache);
547 if (conn->internal->channel_cache)
548 silc_idcache_free(conn->internal->channel_cache);
549 if (conn->internal->server_cache)
550 silc_idcache_free(conn->internal->server_cache);
552 /* Free data (my ID is freed in above silc_client_del_client).
553 conn->nickname is freed when freeing the local_entry->nickname. */
554 silc_free(conn->remote_host);
555 silc_free(conn->local_id_data);
556 if (conn->internal->send_key)
557 silc_cipher_free(conn->internal->send_key);
558 if (conn->internal->receive_key)
559 silc_cipher_free(conn->internal->receive_key);
560 if (conn->internal->hmac_send)
561 silc_hmac_free(conn->internal->hmac_send);
562 if (conn->internal->hmac_receive)
563 silc_hmac_free(conn->internal->hmac_receive);
564 silc_free(conn->internal->rekey);
566 if (conn->internal->active_session) {
568 conn->sock->user_data = NULL;
569 silc_client_ftp_session_free(conn->internal->active_session);
570 conn->internal->active_session = NULL;
573 silc_client_ftp_free_sessions(client, conn);
575 if (conn->internal->pending_commands) {
576 silc_dlist_start(conn->internal->pending_commands);
577 while ((r = silc_dlist_get(conn->internal->pending_commands))
579 silc_dlist_del(conn->internal->pending_commands, r);
580 silc_dlist_uninit(conn->internal->pending_commands);
583 silc_free(conn->internal);
584 memset(conn, 0, sizeof(*conn));
587 silc_dlist_del(client->internal->conns, conn);
593 /******************************* Client API *********************************/
595 /* Connects to remote server. This is the main routine used to connect
596 to remote SILC server. Performs key exchange also. Returns the
597 connection context to the connection callback. */
599 SilcBool silc_client_connect_to_server(SilcClient client,
600 SilcClientConnectionParams *params,
601 SilcPublicKey public_key,
602 SilcPrivateKey private_key,
603 char *remote_host, int port,
604 SilcClientConnectCallback callback,
607 SilcClientConnection conn;
609 if (!client || !remote_host)
612 /* Add new connection */
613 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
614 public_key, private_key, remote_host,
615 port, callback, context);
617 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
621 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
622 "Connecting to port %d of server %s",
625 /* Signal connection machine to start connecting */
626 conn->internal->connect = TRUE;
630 /* Connects to remote client. Performs key exchange also. Returns the
631 connection context to the connection callback. */
633 SilcBool silc_client_connect_to_client(SilcClient client,
634 SilcClientConnectionParams *params,
635 SilcPublicKey public_key,
636 SilcPrivateKey private_key,
637 char *remote_host, int port,
638 SilcClientConnectCallback callback,
641 SilcClientConnection conn;
643 if (!client || !remote_host)
646 /* Add new connection */
647 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
648 public_key, private_key, remote_host,
649 port, callback, context);
651 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
655 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
656 "Connecting to port %d of client host %s",
659 /* Signal connection machine to start connecting */
660 conn->internal->connect = TRUE;
664 /* Starts key exchange in the remote stream indicated by `stream'. This
665 creates the connection context and returns it in the connection callback. */
667 SilcBool silc_client_key_exchange(SilcClient client,
668 SilcClientConnectionParams *params,
669 SilcPublicKey public_key,
670 SilcPrivateKey private_key,
672 SilcConnectionType conn_type,
673 SilcClientConnectCallback callback,
676 SilcClientConnection conn;
680 if (!client || !stream)
683 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port))
686 /* Add new connection */
687 conn = silc_client_add_connection(client, conn_type, params,
688 public_key, private_key,
689 (char *)host, port, callback, context);
691 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
694 conn->stream = (void *)stream;
696 /* Signal connection to start key exchange */
697 conn->internal->key_exchange = TRUE;
701 /* Closes remote connection */
703 void silc_client_close_connection(SilcClient client,
704 SilcClientConnection conn)
710 /* Finalizes the connection to the remote SILC server. This is called
711 after authentication protocol has been completed. This send our
712 user information to the server to receive our client ID from
715 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
717 SilcProtocol protocol = (SilcProtocol)context;
718 SilcClientConnAuthInternalContext *ctx =
719 (SilcClientConnAuthInternalContext *)protocol->context;
720 SilcClient client = (SilcClient)ctx->client;
721 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
724 SILC_LOG_DEBUG(("Start"));
726 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
727 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
728 /* Error occured during protocol */
729 SILC_LOG_DEBUG(("Error during authentication protocol"));
730 ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
734 if (conn->internal->params.detach_data) {
735 /* Send RESUME_CLIENT packet to the server, which is used to resume
736 old detached session back. */
738 SilcClientID *old_client_id;
739 unsigned char *old_id;
740 SilcUInt16 old_id_len;
742 if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
743 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
747 old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
748 if (!old_client_id) {
750 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
754 /* Generate authentication data that server will verify */
755 auth = silc_auth_public_key_auth_generate(client->public_key,
758 conn->internal->hash,
759 old_client_id, SILC_ID_CLIENT);
761 silc_free(old_client_id);
763 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
767 packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
768 silc_buffer_format(packet,
769 SILC_STR_UI_SHORT(old_id_len),
770 SILC_STR_UI_XNSTRING(old_id, old_id_len),
771 SILC_STR_UI_XNSTRING(auth->data, auth->len),
774 /* Send the packet */
775 silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
777 packet->data, packet->len, TRUE);
778 silc_buffer_free(packet);
779 silc_buffer_free(auth);
780 silc_free(old_client_id);
783 /* Send NEW_CLIENT packet to the server. We will become registered
784 to the SILC network after sending this packet and we will receive
785 client ID from the server. */
786 packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
787 strlen(client->realname));
788 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
789 silc_buffer_format(packet,
790 SILC_STR_UI_SHORT(strlen(client->username)),
791 SILC_STR_UI_XNSTRING(client->username,
792 strlen(client->username)),
793 SILC_STR_UI_SHORT(strlen(client->realname)),
794 SILC_STR_UI_XNSTRING(client->realname,
795 strlen(client->realname)),
798 /* Send the packet */
799 silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
801 packet->data, packet->len, TRUE);
802 silc_buffer_free(packet);
805 /* Save remote ID. */
806 conn->remote_id = ctx->dest_id;
807 conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
808 conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
810 /* Register re-key timeout */
811 conn->internal->rekey->timeout = client->internal->params->rekey_secs;
812 conn->internal->rekey->context = (void *)client;
813 silc_schedule_task_add(client->schedule, conn->sock->sock,
814 silc_client_rekey_callback,
815 (void *)conn->sock, conn->internal->rekey->timeout, 0,
816 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
818 silc_protocol_free(protocol);
819 silc_free(ctx->auth_data);
821 silc_ske_free(ctx->ske);
822 silc_socket_free(ctx->sock);
824 conn->sock->protocol = NULL;
828 silc_protocol_free(protocol);
829 silc_free(ctx->auth_data);
830 silc_free(ctx->dest_id);
832 silc_ske_free(ctx->ske);
833 conn->sock->protocol = NULL;
834 silc_socket_free(ctx->sock);
836 /* Notify application of failure */
837 silc_schedule_task_add(client->schedule, ctx->sock->sock,
838 silc_client_connect_failure_auth, ctx,
839 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
842 /* Client session resuming callback. If the session was resumed
843 this callback is called after the resuming is completed. This
844 will call the `connect' client operation to the application
845 since it has not been called yet. */
847 static void silc_client_resume_session_cb(SilcClient client,
848 SilcClientConnection conn,
854 /* Notify application that connection is created to server */
855 client->internal->ops->connected(client, conn, success ?
856 SILC_CLIENT_CONN_SUCCESS_RESUME :
857 SILC_CLIENT_CONN_ERROR_RESUME);
860 /* Issue INFO command to fetch the real server name and server
861 information and other stuff. */
862 silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
863 silc_client_command_reply_info_i, 0,
865 sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
866 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
867 conn->cmd_ident, 1, 2, sidp->data, sidp->len);
868 silc_buffer_free(sidp);
872 /* Processes incoming connection authentication method request packet.
873 It is a reply to our previously sent request. The packet can be used
874 to resolve the authentication method for the current session if the
875 client does not know it beforehand. */
877 void silc_client_connection_auth_request(SilcClient client,
878 SilcClientConnection conn,
879 SilcPacketContext *packet)
881 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
882 SilcUInt16 conn_type, auth_meth;
885 /* If we haven't send our request then ignore this one. */
886 if (!conn->internal->connauth)
889 /* Parse the payload */
890 ret = silc_buffer_unformat(packet->buffer,
891 SILC_STR_UI_SHORT(&conn_type),
892 SILC_STR_UI_SHORT(&auth_meth),
895 auth_meth = SILC_AUTH_NONE;
897 /* Call the request callback to notify application for received
898 authentication method information. */
899 if (conn->internal->connauth->callback)
900 (*conn->internal->connauth->callback)(client, conn, auth_meth,
901 conn->internal->connauth->context);
903 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
905 silc_free(conn->internal->connauth);
906 conn->internal->connauth = NULL;
909 /* Timeout task callback called if the server does not reply to our
910 connection authentication method request in the specified time interval. */
912 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
914 SilcClientConnection conn = (SilcClientConnection)context;
915 SilcClient client = conn->client;
917 if (!conn->internal->connauth)
920 /* Call the request callback to notify application */
921 if (conn->internal->connauth->callback)
922 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
923 conn->internal->connauth->context);
925 silc_free(conn->internal->connauth);
926 conn->internal->connauth = NULL;
929 /* This function can be used to request the current authentication method
930 from the server. This may be called when connecting to the server
931 and the client library requests the authentication data from the
932 application. If the application does not know the current authentication
933 method it can request it from the server using this function.
934 The `callback' with `context' will be called after the server has
935 replied back with the current authentication method. */
938 silc_client_request_authentication_method(SilcClient client,
939 SilcClientConnection conn,
940 SilcConnectionAuthRequest callback,
943 SilcClientConnAuthRequest connauth;
946 assert(client && conn);
947 connauth = silc_calloc(1, sizeof(*connauth));
948 connauth->callback = callback;
949 connauth->context = context;
951 if (conn->internal->connauth)
952 silc_free(conn->internal->connauth);
954 conn->internal->connauth = connauth;
956 /* Assemble the request packet and send it to the server */
957 packet = silc_buffer_alloc(4);
958 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
959 silc_buffer_format(packet,
960 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
961 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
963 silc_client_packet_send(client, conn->sock,
964 SILC_PACKET_CONNECTION_AUTH_REQUEST,
966 packet->data, packet->len, FALSE);
967 silc_buffer_free(packet);
969 /* Register a timeout in case server does not reply anything back. */
971 silc_schedule_task_add(client->schedule, conn->sock->sock,
972 silc_client_request_authentication_method_timeout,
974 client->internal->params->connauth_request_secs, 0,
975 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
980 /* Allocates new client object. This has to be done before client may
981 work. After calling this one must call silc_client_init to initialize
982 the client. The `application' is application specific user data pointer
983 and caller must free it. */
985 SilcClient silc_client_alloc(SilcClientOperations *ops,
986 SilcClientParams *params,
988 const char *version_string)
990 SilcClient new_client;
992 new_client = silc_calloc(1, sizeof(*new_client));
995 new_client->application = application;
997 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
998 if (!new_client->internal) {
999 silc_free(new_client);
1002 new_client->internal->ops = ops;
1003 new_client->internal->params =
1004 silc_calloc(1, sizeof(*new_client->internal->params));
1005 if (!version_string)
1006 version_string = silc_version_string;
1007 new_client->internal->silc_client_version = strdup(version_string);
1010 memcpy(new_client->internal->params, params, sizeof(*params));
1012 if (!new_client->internal->params->task_max)
1013 new_client->internal->params->task_max = 200;
1015 if (!new_client->internal->params->rekey_secs)
1016 new_client->internal->params->rekey_secs = 3600;
1018 if (!new_client->internal->params->connauth_request_secs)
1019 new_client->internal->params->connauth_request_secs = 2;
1021 new_client->internal->params->
1022 nickname_format[sizeof(new_client->internal->
1023 params->nickname_format) - 1] = 0;
1028 /* Frees client object and its internals. */
1030 void silc_client_free(SilcClient client)
1034 silc_rng_free(client->rng);
1036 if (!client->internal->params->dont_register_crypto_library) {
1037 silc_cipher_unregister_all();
1038 silc_pkcs_unregister_all();
1039 silc_hash_unregister_all();
1040 silc_hmac_unregister_all();
1043 silc_free(client->internal->params);
1044 silc_free(client->internal->silc_client_version);
1045 silc_free(client->internal);
1050 /* Initializes the client. This makes all the necessary steps to make
1051 the client ready to be run. One must call silc_client_run to run the
1052 client. Returns FALSE if error occured, TRUE otherwise. */
1054 SilcBool silc_client_init(SilcClient client, const char *username,
1055 const char *hostname, const char *realname)
1057 SILC_LOG_DEBUG(("Initializing client"));
1062 if (!username || !hostname || !realname) {
1063 SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1064 "silc_client_init"));
1068 /* Validate essential strings */
1069 if (!silc_identifier_verify(username, strlen(username),
1070 SILC_STRING_UTF8, 128)) {
1071 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1075 if (!silc_identifier_verify(hostname, strlen(hostname),
1076 SILC_STRING_UTF8, 256)) {
1077 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1081 if (!silc_utf8_valid(realname, strlen(realname))) {
1082 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1087 /* Take the name strings */
1088 client->username = strdup(username);
1089 client->hostname = strdup(hostname);
1090 client->realname = strdup(realname);
1091 if (!username || !hostname || !realname)
1094 if (!client->internal->params->dont_register_crypto_library) {
1095 /* Initialize the crypto library. If application has done this already
1096 this has no effect. Also, we will not be overriding something
1097 application might have registered earlier. */
1098 silc_cipher_register_default();
1099 silc_pkcs_register_default();
1100 silc_hash_register_default();
1101 silc_hmac_register_default();
1104 /* Initialize random number generator */
1105 client->rng = silc_rng_alloc();
1106 silc_rng_init(client->rng);
1107 silc_rng_global_init(client->rng);
1109 /* Initialize the scheduler */
1111 silc_schedule_init(client->internal->params->task_max ?
1112 client->internal->params->task_max : 0, client);
1113 if (!client->schedule)
1116 /* Start packet engine */
1117 client->internal->packet_engine =
1118 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1120 if (!client->internal->packet_engine)
1123 /* Initialize FSM */
1124 if (!silc_fsm_init(&client->internal->fsm, client, NULL, NULL,
1127 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1129 /* Allocate client lock */
1130 silc_mutex_alloc(&client->internal->lock);
1132 /* Register commands */
1133 silc_client_commands_register(client);
1135 /* Start the client machine */
1136 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1138 /* Signal the application when we are running */
1139 client->internal->run_callback = TRUE;
1140 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1145 /* Stops the client. This is called to stop the client and thus to stop
1148 void silc_client_stop(SilcClient client)
1150 SILC_LOG_DEBUG(("Stopping client"));
1152 silc_schedule_stop(client->schedule);
1153 silc_schedule_uninit(client->schedule);
1154 silc_client_commands_unregister(client);
1156 SILC_LOG_DEBUG(("Client stopped"));
1159 /* Starts the SILC client FSM machine and blocks here. When this returns
1160 the client has ended. */
1162 void silc_client_run(SilcClient client)
1164 SILC_LOG_DEBUG(("Starting SILC client"));
1166 /* Run the scheduler */
1167 silc_schedule(client->schedule);
1170 /* Call scheduler one iteration and return. This cannot be called if threads
1173 void silc_client_run_one(SilcClient client)
1175 silc_schedule_one(client->schedule, -1);