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_fsm_next(fsm, silc_client_key_agreement);
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 if (!conn->internal->disconnected) {
392 conn->internal->disconnected = TRUE;
393 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
396 return SILC_FSM_FINISH;
399 /*************************** Main client machine ****************************/
401 /* The client's main state where we wait for various events */
403 SILC_FSM_STATE(silc_client_st_run)
405 SilcClient client = fsm_context;
407 /* Wait for events */
408 SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
412 if (client->internal->run_callback && client->internal->ops->running) {
413 /* Call running callbcak back to application */
414 SILC_LOG_DEBUG(("We are running, call running callback"));
415 client->internal->run_callback = FALSE;
416 client->internal->ops->running(client, client->application);
417 return SILC_FSM_CONTINUE;
422 return SILC_FSM_CONTINUE;
425 /******************************* Private API ********************************/
427 /* Adds new connection. Creates the connection context and returns it. */
429 static SilcClientConnection
430 silc_client_add_connection(SilcClient client,
431 SilcConnectionType conn_type,
432 SilcClientConnectionParams *params,
433 SilcPublicKey public_key,
434 SilcPrivateKey private_key,
435 char *remote_host, int port,
436 SilcClientConnectCallback callback,
439 SilcClientConnection conn;
440 SilcFSMThread thread;
445 SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
447 conn = silc_calloc(1, sizeof(*conn));
451 conn->client = client;
452 conn->public_key = public_key;
453 conn->private_key = private_key;
454 conn->remote_host = strdup(remote_host);
455 conn->remote_port = port ? port : 706;
456 conn->type = conn_type;
457 conn->callback = callback;
458 conn->callback_context = context;
460 conn->internal = silc_calloc(1, sizeof(*conn->internal));
461 if (!conn->internal) {
465 conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
466 silc_mutex_alloc(&conn->internal->lock);
467 silc_atomic_init16(&conn->internal->cmd_ident, 0);
469 if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
471 silc_free(conn->internal);
475 conn->internal->params = *params;
476 conn->internal->verbose = TRUE;
477 silc_list_init(conn->internal->pending_commands,
478 struct SilcClientCommandContextStruct, next);
479 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
481 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
483 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
485 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
487 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
488 !conn->internal->server_cache) {
489 silc_client_del_connection(client, conn);
493 conn->internal->ftp_sessions = silc_dlist_init();
495 /* Run the connection state machine. If threads are in use the machine
496 is always run in a real thread. */
497 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
498 silc_client_fsm_destructor, NULL,
499 client->internal->params->threads);
501 silc_client_del_connection(client, conn);
504 silc_fsm_start(thread, silc_client_connection_st_start);
509 /* Deletes connection. This is always called from the connection machine
510 destructor. Do not call this directly other places. */
512 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
515 SilcIDCacheEntry entry;
516 SilcFSMThread thread;
517 SilcClientCommandContext cmd;
519 SILC_LOG_DEBUG(("Freeing connection %p", conn));
521 /* Free all cache entries */
522 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
523 silc_list_start(list);
524 while ((entry = silc_list_get(list)))
525 silc_client_del_client(client, conn, entry->context);
527 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
528 silc_list_start(list);
529 while ((entry = silc_list_get(list)))
530 silc_client_del_channel(client, conn, entry->context);
532 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
533 silc_list_start(list);
534 while ((entry = silc_list_get(list)))
535 silc_client_del_server(client, conn, entry->context);
539 if (conn->internal->client_cache)
540 silc_idcache_free(conn->internal->client_cache);
541 if (conn->internal->channel_cache)
542 silc_idcache_free(conn->internal->channel_cache);
543 if (conn->internal->server_cache)
544 silc_idcache_free(conn->internal->server_cache);
546 /* Free thread pool */
547 silc_list_start(conn->internal->thread_pool);
548 while ((thread = silc_list_get(conn->internal->thread_pool)))
549 silc_fsm_free(thread);
551 /* Free pending commands */
552 silc_list_start(conn->internal->pending_commands);
553 while ((cmd = silc_list_get(conn->internal->pending_commands)))
554 silc_client_command_free(cmd);
556 silc_free(conn->remote_host);
557 silc_buffer_free(conn->internal->local_idp);
558 silc_buffer_free(conn->internal->remote_idp);
559 silc_mutex_free(conn->internal->lock);
560 if (conn->internal->hash)
561 silc_hash_free(conn->internal->hash);
562 if (conn->internal->sha1hash)
563 silc_hash_free(conn->internal->sha1hash);
564 silc_atomic_uninit16(&conn->internal->cmd_ident);
566 silc_free(conn->internal);
567 memset(conn, 'F', sizeof(*conn));
572 /******************************* Client API *********************************/
574 /* Connects to remote server. This is the main routine used to connect
575 to remote SILC server. Performs key exchange also. Returns the
576 connection context to the connection callback. */
578 SilcBool silc_client_connect_to_server(SilcClient client,
579 SilcClientConnectionParams *params,
580 SilcPublicKey public_key,
581 SilcPrivateKey private_key,
582 char *remote_host, int port,
583 SilcClientConnectCallback callback,
586 SilcClientConnection conn;
588 if (!client || !remote_host)
591 /* Add new connection */
592 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
593 public_key, private_key, remote_host,
594 port, callback, context);
596 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
600 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
601 "Connecting to port %d of server %s",
604 /* Signal connection machine to start connecting */
605 conn->internal->connect = TRUE;
609 /* Connects to remote client. Performs key exchange also. Returns the
610 connection context to the connection callback. */
612 SilcBool silc_client_connect_to_client(SilcClient client,
613 SilcClientConnectionParams *params,
614 SilcPublicKey public_key,
615 SilcPrivateKey private_key,
616 char *remote_host, int port,
617 SilcClientConnectCallback callback,
620 SilcClientConnection conn;
622 if (!client || !remote_host)
625 /* Add new connection */
626 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
627 public_key, private_key, remote_host,
628 port, callback, context);
630 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
634 /* Signal connection machine to start connecting */
635 conn->internal->connect = TRUE;
639 /* Starts key exchange in the remote stream indicated by `stream'. This
640 creates the connection context and returns it in the connection callback. */
642 SilcBool silc_client_key_exchange(SilcClient client,
643 SilcClientConnectionParams *params,
644 SilcPublicKey public_key,
645 SilcPrivateKey private_key,
647 SilcConnectionType conn_type,
648 SilcClientConnectCallback callback,
651 SilcClientConnection conn;
655 if (!client || !stream)
658 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
659 SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
660 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
664 /* Add new connection */
665 conn = silc_client_add_connection(client, conn_type, params,
666 public_key, private_key,
667 (char *)host, port, callback, context);
669 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
672 conn->stream = (void *)stream;
674 /* Signal connection to start key exchange */
675 conn->internal->key_exchange = TRUE;
679 /* Closes remote connection */
681 void silc_client_close_connection(SilcClient client,
682 SilcClientConnection conn)
684 SILC_LOG_DEBUG(("Closing connection %p", conn));
686 /* Signal to close connection */
687 if (!conn->internal->disconnected) {
688 conn->internal->disconnected = TRUE;
689 SILC_FSM_SEMA_POST(&conn->internal->wait_event);
694 /* Finalizes the connection to the remote SILC server. This is called
695 after authentication protocol has been completed. This send our
696 user information to the server to receive our client ID from
699 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
701 SilcProtocol protocol = (SilcProtocol)context;
702 SilcClientConnAuthInternalContext *ctx =
703 (SilcClientConnAuthInternalContext *)protocol->context;
704 SilcClient client = (SilcClient)ctx->client;
705 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
708 SILC_LOG_DEBUG(("Start"));
710 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
711 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
712 /* Error occured during protocol */
713 SILC_LOG_DEBUG(("Error during authentication protocol"));
714 ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
718 if (conn->internal->params.detach_data) {
719 /* Send RESUME_CLIENT packet to the server, which is used to resume
720 old detached session back. */
722 SilcClientID *old_client_id;
723 unsigned char *old_id;
724 SilcUInt16 old_id_len;
726 if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
727 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
731 old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
732 if (!old_client_id) {
734 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
738 /* Generate authentication data that server will verify */
739 auth = silc_auth_public_key_auth_generate(client->public_key,
742 conn->internal->hash,
743 old_client_id, SILC_ID_CLIENT);
745 silc_free(old_client_id);
747 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
751 packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
752 silc_buffer_format(packet,
753 SILC_STR_UI_SHORT(old_id_len),
754 SILC_STR_UI_XNSTRING(old_id, old_id_len),
755 SILC_STR_UI_XNSTRING(auth->data, auth->len),
758 /* Send the packet */
759 silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
761 packet->data, packet->len, TRUE);
762 silc_buffer_free(packet);
763 silc_buffer_free(auth);
764 silc_free(old_client_id);
767 /* Send NEW_CLIENT packet to the server. We will become registered
768 to the SILC network after sending this packet and we will receive
769 client ID from the server. */
770 packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
771 strlen(client->realname));
772 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
773 silc_buffer_format(packet,
774 SILC_STR_UI_SHORT(strlen(client->username)),
775 SILC_STR_UI_XNSTRING(client->username,
776 strlen(client->username)),
777 SILC_STR_UI_SHORT(strlen(client->realname)),
778 SILC_STR_UI_XNSTRING(client->realname,
779 strlen(client->realname)),
782 /* Send the packet */
783 silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
785 packet->data, packet->len, TRUE);
786 silc_buffer_free(packet);
789 /* Save remote ID. */
790 conn->remote_id = ctx->dest_id;
791 conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
792 conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
794 /* Register re-key timeout */
795 conn->internal->rekey->timeout = client->internal->params->rekey_secs;
796 conn->internal->rekey->context = (void *)client;
797 silc_schedule_task_add(client->schedule, conn->sock->sock,
798 silc_client_rekey_callback,
799 (void *)conn->sock, conn->internal->rekey->timeout, 0,
800 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
802 silc_protocol_free(protocol);
803 silc_free(ctx->auth_data);
805 silc_ske_free(ctx->ske);
806 silc_socket_free(ctx->sock);
808 conn->sock->protocol = NULL;
812 silc_protocol_free(protocol);
813 silc_free(ctx->auth_data);
814 silc_free(ctx->dest_id);
816 silc_ske_free(ctx->ske);
817 conn->sock->protocol = NULL;
818 silc_socket_free(ctx->sock);
820 /* Notify application of failure */
821 silc_schedule_task_add(client->schedule, ctx->sock->sock,
822 silc_client_connect_failure_auth, ctx,
823 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
826 /* Client session resuming callback. If the session was resumed
827 this callback is called after the resuming is completed. This
828 will call the `connect' client operation to the application
829 since it has not been called yet. */
831 static void silc_client_resume_session_cb(SilcClient client,
832 SilcClientConnection conn,
838 /* Notify application that connection is created to server */
839 client->internal->ops->connected(client, conn, success ?
840 SILC_CLIENT_CONN_SUCCESS_RESUME :
841 SILC_CLIENT_CONN_ERROR_RESUME);
844 /* Issue INFO command to fetch the real server name and server
845 information and other stuff. */
846 silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
847 silc_client_command_reply_info_i, 0,
849 sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
850 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
851 conn->cmd_ident, 1, 2, sidp->data, sidp->len);
852 silc_buffer_free(sidp);
856 /* Processes incoming connection authentication method request packet.
857 It is a reply to our previously sent request. The packet can be used
858 to resolve the authentication method for the current session if the
859 client does not know it beforehand. */
861 void silc_client_connection_auth_request(SilcClient client,
862 SilcClientConnection conn,
863 SilcPacketContext *packet)
865 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
866 SilcUInt16 conn_type, auth_meth;
869 /* If we haven't send our request then ignore this one. */
870 if (!conn->internal->connauth)
873 /* Parse the payload */
874 ret = silc_buffer_unformat(packet->buffer,
875 SILC_STR_UI_SHORT(&conn_type),
876 SILC_STR_UI_SHORT(&auth_meth),
879 auth_meth = SILC_AUTH_NONE;
881 /* Call the request callback to notify application for received
882 authentication method information. */
883 if (conn->internal->connauth->callback)
884 (*conn->internal->connauth->callback)(client, conn, auth_meth,
885 conn->internal->connauth->context);
887 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
889 silc_free(conn->internal->connauth);
890 conn->internal->connauth = NULL;
893 /* Timeout task callback called if the server does not reply to our
894 connection authentication method request in the specified time interval. */
896 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
898 SilcClientConnection conn = (SilcClientConnection)context;
899 SilcClient client = conn->client;
901 if (!conn->internal->connauth)
904 /* Call the request callback to notify application */
905 if (conn->internal->connauth->callback)
906 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
907 conn->internal->connauth->context);
909 silc_free(conn->internal->connauth);
910 conn->internal->connauth = NULL;
913 /* This function can be used to request the current authentication method
914 from the server. This may be called when connecting to the server
915 and the client library requests the authentication data from the
916 application. If the application does not know the current authentication
917 method it can request it from the server using this function.
918 The `callback' with `context' will be called after the server has
919 replied back with the current authentication method. */
922 silc_client_request_authentication_method(SilcClient client,
923 SilcClientConnection conn,
924 SilcConnectionAuthRequest callback,
927 SilcClientConnAuthRequest connauth;
930 assert(client && conn);
931 connauth = silc_calloc(1, sizeof(*connauth));
932 connauth->callback = callback;
933 connauth->context = context;
935 if (conn->internal->connauth)
936 silc_free(conn->internal->connauth);
938 conn->internal->connauth = connauth;
940 /* Assemble the request packet and send it to the server */
941 packet = silc_buffer_alloc(4);
942 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
943 silc_buffer_format(packet,
944 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
945 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
947 silc_client_packet_send(client, conn->sock,
948 SILC_PACKET_CONNECTION_AUTH_REQUEST,
950 packet->data, packet->len, FALSE);
951 silc_buffer_free(packet);
953 /* Register a timeout in case server does not reply anything back. */
955 silc_schedule_task_add(client->schedule, conn->sock->sock,
956 silc_client_request_authentication_method_timeout,
958 client->internal->params->connauth_request_secs, 0,
959 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
964 /* Allocates new client object. This has to be done before client may
965 work. After calling this one must call silc_client_init to initialize
966 the client. The `application' is application specific user data pointer
967 and caller must free it. */
969 SilcClient silc_client_alloc(SilcClientOperations *ops,
970 SilcClientParams *params,
972 const char *version_string)
974 SilcClient new_client;
976 new_client = silc_calloc(1, sizeof(*new_client));
979 new_client->application = application;
981 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
982 if (!new_client->internal) {
983 silc_free(new_client);
986 new_client->internal->ops = ops;
987 new_client->internal->params =
988 silc_calloc(1, sizeof(*new_client->internal->params));
990 version_string = silc_version_string;
991 new_client->internal->silc_client_version = strdup(version_string);
994 memcpy(new_client->internal->params, params, sizeof(*params));
996 if (!new_client->internal->params->rekey_secs)
997 new_client->internal->params->rekey_secs = 3600;
999 if (!new_client->internal->params->connauth_request_secs)
1000 new_client->internal->params->connauth_request_secs = 2;
1002 new_client->internal->params->
1003 nickname_format[sizeof(new_client->internal->
1004 params->nickname_format) - 1] = 0;
1009 /* Frees client object and its internals. */
1011 void silc_client_free(SilcClient client)
1014 silc_rng_free(client->rng);
1016 if (!client->internal->params->dont_register_crypto_library) {
1017 silc_cipher_unregister_all();
1018 silc_pkcs_unregister_all();
1019 silc_hash_unregister_all();
1020 silc_hmac_unregister_all();
1023 silc_free(client->username);
1024 silc_free(client->hostname);
1025 silc_free(client->realname);
1026 silc_free(client->internal->params);
1027 silc_free(client->internal->silc_client_version);
1028 silc_free(client->internal);
1032 /* Initializes the client. This makes all the necessary steps to make
1033 the client ready to be run. One must call silc_client_run to run the
1034 client. Returns FALSE if error occured, TRUE otherwise. */
1036 SilcBool silc_client_init(SilcClient client, const char *username,
1037 const char *hostname, const char *realname)
1039 SILC_LOG_DEBUG(("Initializing client"));
1044 if (!username || !hostname) {
1045 SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1046 "silc_client_init"));
1050 realname = username;
1052 /* Validate essential strings */
1053 if (!silc_identifier_verify(username, strlen(username),
1054 SILC_STRING_UTF8, 128)) {
1055 SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1059 if (!silc_identifier_verify(hostname, strlen(hostname),
1060 SILC_STRING_UTF8, 256)) {
1061 SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1065 if (!silc_utf8_valid(realname, strlen(realname))) {
1066 SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1071 /* Take the name strings */
1072 client->username = strdup(username);
1073 client->hostname = strdup(hostname);
1074 client->realname = strdup(realname);
1075 if (!username || !hostname || !realname)
1078 if (!client->internal->params->dont_register_crypto_library) {
1079 /* Initialize the crypto library. If application has done this already
1080 this has no effect. Also, we will not be overriding something
1081 application might have registered earlier. */
1082 silc_cipher_register_default();
1083 silc_pkcs_register_default();
1084 silc_hash_register_default();
1085 silc_hmac_register_default();
1088 /* Initialize random number generator */
1089 client->rng = silc_rng_alloc();
1092 silc_rng_init(client->rng);
1093 silc_rng_global_init(client->rng);
1095 /* Initialize the scheduler */
1096 client->schedule = silc_schedule_init(0, client);
1097 if (!client->schedule)
1100 /* Start packet engine */
1101 client->internal->packet_engine =
1102 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1104 if (!client->internal->packet_engine)
1107 /* Allocate client lock */
1108 silc_mutex_alloc(&client->internal->lock);
1110 /* Register commands */
1111 silc_client_commands_register(client);
1113 /* Initialize and start the client FSM */
1114 silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1115 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1116 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1118 /* Signal the application when we are running */
1119 client->internal->run_callback = TRUE;
1120 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1125 /* Stops the client. This is called to stop the client and thus to stop
1128 void silc_client_stop(SilcClient client)
1130 SILC_LOG_DEBUG(("Stopping client"));
1132 silc_schedule_stop(client->schedule);
1133 silc_schedule_uninit(client->schedule);
1134 silc_client_commands_unregister(client);
1136 SILC_LOG_DEBUG(("Client stopped"));
1139 /* Starts the SILC client FSM machine and blocks here. When this returns
1140 the client has ended. */
1142 void silc_client_run(SilcClient client)
1144 SILC_LOG_DEBUG(("Starting SILC client"));
1146 /* Run the scheduler */
1147 silc_schedule(client->schedule);
1150 /* Call scheduler one iteration and return. This cannot be called if threads
1153 void silc_client_run_one(SilcClient client)
1155 silc_schedule_one(client->schedule, 0);