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);
298 case SILC_PACKET_CONNECTION_AUTH_REQUEST:
299 /* Reply to connection authentication request to resolve authentication
300 method from server. */
301 // silc_client_connection_auth_request(client, conn, packet);
305 silc_packet_free(packet);
306 return SILC_FSM_FINISH;
310 return SILC_FSM_CONTINUE;
313 /* Disconnection even to close remote connection. We close the connection
314 and finish the connection machine in this state. The connection context
315 is deleted in the machine destructor. The connection callback must be
316 already called back to application before getting here. */
318 SILC_FSM_STATE(silc_client_connection_st_close)
320 SilcClientConnection conn = fsm_context;
322 SILC_LOG_DEBUG(("Closing remote connection"));
324 /* Abort ongoing events */
325 if (conn->internal->op)
326 silc_async_abort(conn->internal->op, NULL, NULL);
328 /* Close connection */
329 silc_packet_stream_destroy(conn->stream);
331 SILC_LOG_DEBUG(("Finishing connection machine"));
333 return SILC_FSM_FINISH;
336 /* Received error packet from server. Send it to application. */
338 SILC_FSM_STATE(silc_client_error)
340 SilcClientConnection conn = fsm_context;
341 SilcClient client = conn->client;
342 SilcPacket packet = state_context;
345 msg = silc_memdup(silc_buffer_data(&packet->buffer),
346 silc_buffer_len(&packet->buffer));
348 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
351 silc_packet_free(packet);
353 return SILC_FSM_FINISH;
356 /* Received disconnect packet from server. We close the connection and
357 send the disconnect message to application. */
359 SILC_FSM_STATE(silc_client_disconnect)
361 SilcClientConnection conn = fsm_context;
362 SilcClient client = conn->client;
363 SilcPacket packet = state_context;
365 char *message = NULL;
367 SILC_LOG_DEBUG(("Server disconnected"));
369 if (silc_buffer_len(&packet->buffer) < 1) {
370 silc_packet_free(packet);
371 return SILC_FSM_FINISH;
374 status = (SilcStatus)packet->buffer.data[0];
376 silc_buffer_pull(&packet->buffer, 1);
377 if (silc_buffer_len(&packet->buffer) > 1 &&
378 silc_utf8_valid(silc_buffer_data(&packet->buffer),
379 silc_buffer_len(&packet->buffer)))
380 message = silc_memdup(silc_buffer_data(&packet->buffer),
381 silc_buffer_len(&packet->buffer));
383 /* Call connection callback */
384 conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
385 message, conn->callback_context);
388 silc_packet_free(packet);
390 /* Signal to close connection */
391 conn->internal->disconnected = TRUE;
392 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
394 return SILC_FSM_FINISH;
397 /*************************** Main client machine ****************************/
399 /* The client's main state where we wait for various events */
401 SILC_FSM_STATE(silc_client_st_run)
403 SilcClient client = fsm_context;
405 /* Wait for events */
406 SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
410 if (client->internal->run_callback && client->internal->ops->running) {
411 /* Call running callbcak back to application */
412 SILC_LOG_DEBUG(("We are running, call running callback"));
413 client->internal->run_callback = FALSE;
414 client->internal->ops->running(client, client->application);
415 return SILC_FSM_CONTINUE;
420 return SILC_FSM_CONTINUE;
423 /******************************* Private API ********************************/
425 /* Adds new connection. Creates the connection context and returns it. */
427 static SilcClientConnection
428 silc_client_add_connection(SilcClient client,
429 SilcConnectionType conn_type,
430 SilcClientConnectionParams *params,
431 SilcPublicKey public_key,
432 SilcPrivateKey private_key,
433 char *remote_host, int port,
434 SilcClientConnectCallback callback,
437 SilcClientConnection conn;
438 SilcFSMThread thread;
443 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
445 conn = silc_calloc(1, sizeof(*conn));
449 conn->client = client;
450 conn->public_key = public_key;
451 conn->private_key = private_key;
452 conn->remote_host = strdup(remote_host);
453 conn->remote_port = port ? port : 706;
454 conn->type = conn_type;
455 conn->callback = callback;
456 conn->callback_context = context;
458 conn->internal = silc_calloc(1, sizeof(*conn->internal));
459 if (!conn->internal) {
463 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
464 silc_mutex_alloc(&conn->internal->lock);
465 silc_atomic_init16(&conn->internal->cmd_ident, 0);
467 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
469 silc_free(conn->internal);
473 conn->internal->params = *params;
474 conn->internal->verbose = TRUE;
475 silc_list_init(conn->internal->pending_commands,
476 struct SilcClientCommandContextStruct, next);
477 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
479 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
481 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
483 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
485 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
486 !conn->internal->server_cache) {
487 silc_client_del_connection(client, conn);
491 conn->internal->ftp_sessions = silc_dlist_init();
493 /* Run the connection state machine. If threads are in use the machine
494 is always run in a real thread. */
495 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
496 silc_client_fsm_destructor, NULL,
497 client->internal->params->threads);
499 silc_client_del_connection(client, conn);
502 silc_fsm_start(thread, silc_client_connection_st_start);
507 /* Deletes connection. This is always called from the connection machine
508 destructor. Do not call this directly other places. */
510 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
513 SilcIDCacheEntry entry;
514 SilcFSMThread thread;
515 SilcClientCommandContext cmd;
517 SILC_LOG_DEBUG(("Freeing connection %p", conn));
519 /* Free all cache entries */
520 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
521 silc_list_start(list);
522 while ((entry = silc_list_get(list)))
523 silc_client_del_client(client, conn, entry->context);
525 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
526 silc_list_start(list);
527 while ((entry = silc_list_get(list)))
528 silc_client_del_channel(client, conn, entry->context);
530 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
531 silc_list_start(list);
532 while ((entry = silc_list_get(list)))
533 silc_client_del_server(client, conn, entry->context);
537 if (conn->internal->client_cache)
538 silc_idcache_free(conn->internal->client_cache);
539 if (conn->internal->channel_cache)
540 silc_idcache_free(conn->internal->channel_cache);
541 if (conn->internal->server_cache)
542 silc_idcache_free(conn->internal->server_cache);
544 /* Free thread pool */
545 silc_list_start(conn->internal->thread_pool);
546 while ((thread = silc_list_get(conn->internal->thread_pool)))
547 silc_fsm_free(thread);
549 /* Free pending commands */
550 silc_list_start(conn->internal->pending_commands);
551 while ((cmd = silc_list_get(conn->internal->pending_commands)))
552 silc_client_command_free(cmd);
554 silc_free(conn->remote_host);
555 silc_buffer_free(conn->internal->local_idp);
556 silc_buffer_free(conn->internal->remote_idp);
557 silc_mutex_free(conn->internal->lock);
558 if (conn->internal->hash)
559 silc_hash_free(conn->internal->hash);
560 if (conn->internal->sha1hash)
561 silc_hash_free(conn->internal->sha1hash);
562 silc_atomic_uninit16(&conn->internal->cmd_ident);
564 silc_free(conn->internal);
565 memset(conn, 'F', sizeof(*conn));
570 /******************************* Client API *********************************/
572 /* Connects to remote server. This is the main routine used to connect
573 to remote SILC server. Performs key exchange also. Returns the
574 connection context to the connection callback. */
576 SilcBool silc_client_connect_to_server(SilcClient client,
577 SilcClientConnectionParams *params,
578 SilcPublicKey public_key,
579 SilcPrivateKey private_key,
580 char *remote_host, int port,
581 SilcClientConnectCallback callback,
584 SilcClientConnection conn;
586 if (!client || !remote_host)
589 /* Add new connection */
590 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
591 public_key, private_key, remote_host,
592 port, callback, context);
594 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
598 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
599 "Connecting to port %d of server %s",
602 /* Signal connection machine to start connecting */
603 conn->internal->connect = TRUE;
607 /* Connects to remote client. Performs key exchange also. Returns the
608 connection context to the connection callback. */
610 SilcBool silc_client_connect_to_client(SilcClient client,
611 SilcClientConnectionParams *params,
612 SilcPublicKey public_key,
613 SilcPrivateKey private_key,
614 char *remote_host, int port,
615 SilcClientConnectCallback callback,
618 SilcClientConnection conn;
620 if (!client || !remote_host)
623 /* Add new connection */
624 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
625 public_key, private_key, remote_host,
626 port, callback, context);
628 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
632 /* Signal connection machine to start connecting */
633 conn->internal->connect = TRUE;
637 /* Starts key exchange in the remote stream indicated by `stream'. This
638 creates the connection context and returns it in the connection callback. */
640 SilcBool silc_client_key_exchange(SilcClient client,
641 SilcClientConnectionParams *params,
642 SilcPublicKey public_key,
643 SilcPrivateKey private_key,
645 SilcConnectionType conn_type,
646 SilcClientConnectCallback callback,
649 SilcClientConnection conn;
653 if (!client || !stream)
656 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
657 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
658 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
662 /* Add new connection */
663 conn = silc_client_add_connection(client, conn_type, params,
664 public_key, private_key,
665 (char *)host, port, callback, context);
667 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
670 conn->stream = (void *)stream;
672 /* Signal connection to start key exchange */
673 conn->internal->key_exchange = TRUE;
677 /* Closes remote connection */
679 void silc_client_close_connection(SilcClient client,
680 SilcClientConnection conn)
682 SILC_LOG_DEBUG(("Closing connection %p", conn));
684 /* Signal to close connection */
685 conn->internal->disconnected = TRUE;
686 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
690 /* Finalizes the connection to the remote SILC server. This is called
691 after authentication protocol has been completed. This send our
692 user information to the server to receive our client ID from
695 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
697 SilcProtocol protocol = (SilcProtocol)context;
698 SilcClientConnAuthInternalContext *ctx =
699 (SilcClientConnAuthInternalContext *)protocol->context;
700 SilcClient client = (SilcClient)ctx->client;
701 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
704 SILC_LOG_DEBUG(("Start"));
706 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
707 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
708 /* Error occured during protocol */
709 SILC_LOG_DEBUG(("Error during authentication protocol"));
710 ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
714 if (conn->internal->params.detach_data) {
715 /* Send RESUME_CLIENT packet to the server, which is used to resume
716 old detached session back. */
718 SilcClientID *old_client_id;
719 unsigned char *old_id;
720 SilcUInt16 old_id_len;
722 if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
723 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
727 old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
728 if (!old_client_id) {
730 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
734 /* Generate authentication data that server will verify */
735 auth = silc_auth_public_key_auth_generate(client->public_key,
738 conn->internal->hash,
739 old_client_id, SILC_ID_CLIENT);
741 silc_free(old_client_id);
743 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
747 packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
748 silc_buffer_format(packet,
749 SILC_STR_UI_SHORT(old_id_len),
750 SILC_STR_UI_XNSTRING(old_id, old_id_len),
751 SILC_STR_UI_XNSTRING(auth->data, auth->len),
754 /* Send the packet */
755 silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
757 packet->data, packet->len, TRUE);
758 silc_buffer_free(packet);
759 silc_buffer_free(auth);
760 silc_free(old_client_id);
763 /* Send NEW_CLIENT packet to the server. We will become registered
764 to the SILC network after sending this packet and we will receive
765 client ID from the server. */
766 packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
767 strlen(client->realname));
768 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
769 silc_buffer_format(packet,
770 SILC_STR_UI_SHORT(strlen(client->username)),
771 SILC_STR_UI_XNSTRING(client->username,
772 strlen(client->username)),
773 SILC_STR_UI_SHORT(strlen(client->realname)),
774 SILC_STR_UI_XNSTRING(client->realname,
775 strlen(client->realname)),
778 /* Send the packet */
779 silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
781 packet->data, packet->len, TRUE);
782 silc_buffer_free(packet);
785 /* Save remote ID. */
786 conn->remote_id = ctx->dest_id;
787 conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
788 conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
790 /* Register re-key timeout */
791 conn->internal->rekey->timeout = client->internal->params->rekey_secs;
792 conn->internal->rekey->context = (void *)client;
793 silc_schedule_task_add(client->schedule, conn->sock->sock,
794 silc_client_rekey_callback,
795 (void *)conn->sock, conn->internal->rekey->timeout, 0,
796 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
798 silc_protocol_free(protocol);
799 silc_free(ctx->auth_data);
801 silc_ske_free(ctx->ske);
802 silc_socket_free(ctx->sock);
804 conn->sock->protocol = NULL;
808 silc_protocol_free(protocol);
809 silc_free(ctx->auth_data);
810 silc_free(ctx->dest_id);
812 silc_ske_free(ctx->ske);
813 conn->sock->protocol = NULL;
814 silc_socket_free(ctx->sock);
816 /* Notify application of failure */
817 silc_schedule_task_add(client->schedule, ctx->sock->sock,
818 silc_client_connect_failure_auth, ctx,
819 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
822 /* Client session resuming callback. If the session was resumed
823 this callback is called after the resuming is completed. This
824 will call the `connect' client operation to the application
825 since it has not been called yet. */
827 static void silc_client_resume_session_cb(SilcClient client,
828 SilcClientConnection conn,
834 /* Notify application that connection is created to server */
835 client->internal->ops->connected(client, conn, success ?
836 SILC_CLIENT_CONN_SUCCESS_RESUME :
837 SILC_CLIENT_CONN_ERROR_RESUME);
840 /* Issue INFO command to fetch the real server name and server
841 information and other stuff. */
842 silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
843 silc_client_command_reply_info_i, 0,
845 sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
846 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
847 conn->cmd_ident, 1, 2, sidp->data, sidp->len);
848 silc_buffer_free(sidp);
852 /* Processes incoming connection authentication method request packet.
853 It is a reply to our previously sent request. The packet can be used
854 to resolve the authentication method for the current session if the
855 client does not know it beforehand. */
857 void silc_client_connection_auth_request(SilcClient client,
858 SilcClientConnection conn,
859 SilcPacketContext *packet)
861 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
862 SilcUInt16 conn_type, auth_meth;
865 /* If we haven't send our request then ignore this one. */
866 if (!conn->internal->connauth)
869 /* Parse the payload */
870 ret = silc_buffer_unformat(packet->buffer,
871 SILC_STR_UI_SHORT(&conn_type),
872 SILC_STR_UI_SHORT(&auth_meth),
875 auth_meth = SILC_AUTH_NONE;
877 /* Call the request callback to notify application for received
878 authentication method information. */
879 if (conn->internal->connauth->callback)
880 (*conn->internal->connauth->callback)(client, conn, auth_meth,
881 conn->internal->connauth->context);
883 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
885 silc_free(conn->internal->connauth);
886 conn->internal->connauth = NULL;
889 /* Timeout task callback called if the server does not reply to our
890 connection authentication method request in the specified time interval. */
892 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
894 SilcClientConnection conn = (SilcClientConnection)context;
895 SilcClient client = conn->client;
897 if (!conn->internal->connauth)
900 /* Call the request callback to notify application */
901 if (conn->internal->connauth->callback)
902 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
903 conn->internal->connauth->context);
905 silc_free(conn->internal->connauth);
906 conn->internal->connauth = NULL;
909 /* This function can be used to request the current authentication method
910 from the server. This may be called when connecting to the server
911 and the client library requests the authentication data from the
912 application. If the application does not know the current authentication
913 method it can request it from the server using this function.
914 The `callback' with `context' will be called after the server has
915 replied back with the current authentication method. */
918 silc_client_request_authentication_method(SilcClient client,
919 SilcClientConnection conn,
920 SilcConnectionAuthRequest callback,
923 SilcClientConnAuthRequest connauth;
926 assert(client && conn);
927 connauth = silc_calloc(1, sizeof(*connauth));
928 connauth->callback = callback;
929 connauth->context = context;
931 if (conn->internal->connauth)
932 silc_free(conn->internal->connauth);
934 conn->internal->connauth = connauth;
936 /* Assemble the request packet and send it to the server */
937 packet = silc_buffer_alloc(4);
938 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
939 silc_buffer_format(packet,
940 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
941 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
943 silc_client_packet_send(client, conn->sock,
944 SILC_PACKET_CONNECTION_AUTH_REQUEST,
946 packet->data, packet->len, FALSE);
947 silc_buffer_free(packet);
949 /* Register a timeout in case server does not reply anything back. */
951 silc_schedule_task_add(client->schedule, conn->sock->sock,
952 silc_client_request_authentication_method_timeout,
954 client->internal->params->connauth_request_secs, 0,
955 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
960 /* Allocates new client object. This has to be done before client may
961 work. After calling this one must call silc_client_init to initialize
962 the client. The `application' is application specific user data pointer
963 and caller must free it. */
965 SilcClient silc_client_alloc(SilcClientOperations *ops,
966 SilcClientParams *params,
968 const char *version_string)
970 SilcClient new_client;
972 new_client = silc_calloc(1, sizeof(*new_client));
975 new_client->application = application;
977 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
978 if (!new_client->internal) {
979 silc_free(new_client);
982 new_client->internal->ops = ops;
983 new_client->internal->params =
984 silc_calloc(1, sizeof(*new_client->internal->params));
986 version_string = silc_version_string;
987 new_client->internal->silc_client_version = strdup(version_string);
990 memcpy(new_client->internal->params, params, sizeof(*params));
992 if (!new_client->internal->params->rekey_secs)
993 new_client->internal->params->rekey_secs = 3600;
995 if (!new_client->internal->params->connauth_request_secs)
996 new_client->internal->params->connauth_request_secs = 2;
998 new_client->internal->params->
999 nickname_format[sizeof(new_client->internal->
1000 params->nickname_format) - 1] = 0;
1005 /* Frees client object and its internals. */
1007 void silc_client_free(SilcClient client)
1010 silc_rng_free(client->rng);
1012 if (!client->internal->params->dont_register_crypto_library) {
1013 silc_cipher_unregister_all();
1014 silc_pkcs_unregister_all();
1015 silc_hash_unregister_all();
1016 silc_hmac_unregister_all();
1019 silc_free(client->username);
1020 silc_free(client->hostname);
1021 silc_free(client->realname);
1022 silc_free(client->internal->params);
1023 silc_free(client->internal->silc_client_version);
1024 silc_free(client->internal);
1028 /* Initializes the client. This makes all the necessary steps to make
1029 the client ready to be run. One must call silc_client_run to run the
1030 client. Returns FALSE if error occured, TRUE otherwise. */
1032 SilcBool silc_client_init(SilcClient client, const char *username,
1033 const char *hostname, const char *realname)
1035 SILC_LOG_DEBUG(("Initializing client"));
1040 if (!username || !hostname) {
1041 SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1042 "silc_client_init"));
1046 realname = username;
1048 /* Validate essential strings */
1049 if (!silc_identifier_verify(username, strlen(username),
1050 SILC_STRING_UTF8, 128)) {
1051 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1055 if (!silc_identifier_verify(hostname, strlen(hostname),
1056 SILC_STRING_UTF8, 256)) {
1057 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1061 if (!silc_utf8_valid(realname, strlen(realname))) {
1062 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1067 /* Take the name strings */
1068 client->username = strdup(username);
1069 client->hostname = strdup(hostname);
1070 client->realname = strdup(realname);
1071 if (!username || !hostname || !realname)
1074 if (!client->internal->params->dont_register_crypto_library) {
1075 /* Initialize the crypto library. If application has done this already
1076 this has no effect. Also, we will not be overriding something
1077 application might have registered earlier. */
1078 silc_cipher_register_default();
1079 silc_pkcs_register_default();
1080 silc_hash_register_default();
1081 silc_hmac_register_default();
1084 /* Initialize random number generator */
1085 client->rng = silc_rng_alloc();
1088 silc_rng_init(client->rng);
1089 silc_rng_global_init(client->rng);
1091 /* Initialize the scheduler */
1092 client->schedule = silc_schedule_init(0, client);
1093 if (!client->schedule)
1096 /* Start packet engine */
1097 client->internal->packet_engine =
1098 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1100 if (!client->internal->packet_engine)
1103 /* Allocate client lock */
1104 silc_mutex_alloc(&client->internal->lock);
1106 /* Register commands */
1107 silc_client_commands_register(client);
1109 /* Initialize and start the client FSM */
1110 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1111 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1112 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1114 /* Signal the application when we are running */
1115 client->internal->run_callback = TRUE;
1116 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1121 /* Stops the client. This is called to stop the client and thus to stop
1124 void silc_client_stop(SilcClient client)
1126 SILC_LOG_DEBUG(("Stopping client"));
1128 silc_schedule_stop(client->schedule);
1129 silc_schedule_uninit(client->schedule);
1130 silc_client_commands_unregister(client);
1132 SILC_LOG_DEBUG(("Client stopped"));
1135 /* Starts the SILC client FSM machine and blocks here. When this returns
1136 the client has ended. */
1138 void silc_client_run(SilcClient client)
1140 SILC_LOG_DEBUG(("Starting SILC client"));
1142 /* Run the scheduler */
1143 silc_schedule(client->schedule);
1146 /* Call scheduler one iteration and return. This cannot be called if threads
1149 void silc_client_run_one(SilcClient client)
1151 silc_schedule_one(client->schedule, 0);