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->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));
444 conn->internal = silc_calloc(1, sizeof(*conn->internal));
445 if (!conn->internal) {
450 conn->client = client;
451 conn->public_key = public_key;
452 conn->private_key = private_key;
453 conn->remote_host = strdup(remote_host);
454 conn->remote_port = port ? port : 706;
455 conn->type = conn_type;
456 conn->callback = callback;
457 conn->context = context;
458 conn->internal->verbose = TRUE;
459 silc_list_init(conn->internal->pending_commands,
460 struct SilcClientCommandContextStruct, next);
461 silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
463 conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
465 conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
467 conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
469 if (!conn->internal->client_cache || !conn->internal->channel_cache ||
470 !conn->internal->server_cache) {
471 silc_client_del_connection(client, conn);
475 conn->internal->ftp_sessions = silc_dlist_init();
478 if (params->detach_data)
479 conn->internal->params.detach_data =
480 silc_memdup(params->detach_data,
481 params->detach_data_len);
482 conn->internal->params.detach_data_len = params->detach_data_len;
485 /* Run the connection state machine. If threads are in use the machine
486 is always run in a real thread. */
487 thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
488 silc_client_fsm_destructor, NULL,
489 client->internal->params->threads);
491 silc_client_del_connection(client, conn);
494 silc_fsm_start(thread, silc_client_connection_st_start);
499 /* Removes connection from client. Frees all memory. */
501 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
504 SilcClientConnection c;
505 SilcIDCacheList list;
506 SilcIDCacheEntry entry;
507 SilcClientCommandPending *r;
510 silc_dlist_start(client->internal->conns);
511 while ((c = silc_dlist_get(client->internal->conns)) != SILC_LIST_END) {
515 /* Free all cache entries */
516 if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
517 ret = silc_idcache_list_first(list, &entry);
519 silc_client_del_client(client, conn, entry->context);
520 ret = silc_idcache_list_next(list, &entry);
522 silc_idcache_list_free(list);
525 if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
526 ret = silc_idcache_list_first(list, &entry);
528 silc_client_del_channel(client, conn, entry->context);
529 ret = silc_idcache_list_next(list, &entry);
531 silc_idcache_list_free(list);
534 if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
535 ret = silc_idcache_list_first(list, &entry);
537 silc_client_del_server(client, conn, entry->context);
538 ret = silc_idcache_list_next(list, &entry);
540 silc_idcache_list_free(list);
543 /* Clear ID caches */
544 if (conn->internal->client_cache)
545 silc_idcache_free(conn->internal->client_cache);
546 if (conn->internal->channel_cache)
547 silc_idcache_free(conn->internal->channel_cache);
548 if (conn->internal->server_cache)
549 silc_idcache_free(conn->internal->server_cache);
551 /* Free data (my ID is freed in above silc_client_del_client).
552 conn->nickname is freed when freeing the local_entry->nickname. */
553 silc_free(conn->remote_host);
554 silc_free(conn->local_id_data);
555 if (conn->internal->send_key)
556 silc_cipher_free(conn->internal->send_key);
557 if (conn->internal->receive_key)
558 silc_cipher_free(conn->internal->receive_key);
559 if (conn->internal->hmac_send)
560 silc_hmac_free(conn->internal->hmac_send);
561 if (conn->internal->hmac_receive)
562 silc_hmac_free(conn->internal->hmac_receive);
563 silc_free(conn->internal->rekey);
565 if (conn->internal->active_session) {
567 conn->sock->user_data = NULL;
568 silc_client_ftp_session_free(conn->internal->active_session);
569 conn->internal->active_session = NULL;
572 silc_client_ftp_free_sessions(client, conn);
574 if (conn->internal->pending_commands) {
575 silc_dlist_start(conn->internal->pending_commands);
576 while ((r = silc_dlist_get(conn->internal->pending_commands))
578 silc_dlist_del(conn->internal->pending_commands, r);
579 silc_dlist_uninit(conn->internal->pending_commands);
582 silc_free(conn->internal);
583 memset(conn, 0, sizeof(*conn));
586 silc_dlist_del(client->internal->conns, conn);
592 /******************************* Client API *********************************/
594 /* Connects to remote server. This is the main routine used to connect
595 to remote SILC server. Performs key exchange also. Returns the
596 connection context to the connection callback. */
598 SilcBool silc_client_connect_to_server(SilcClient client,
599 SilcClientConnectionParams *params,
600 SilcPublicKey public_key,
601 SilcPrivateKey private_key,
602 char *remote_host, int port,
603 SilcClientConnectCallback callback,
606 SilcClientConnection conn;
608 if (!client || !remote_host)
611 /* Add new connection */
612 conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
613 public_key, private_key, remote_host,
614 port, callback, context);
616 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
620 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
621 "Connecting to port %d of server %s",
624 /* Signal connection machine to start connecting */
625 conn->internal->connect = TRUE;
629 /* Connects to remote client. Performs key exchange also. Returns the
630 connection context to the connection callback. */
632 SilcBool silc_client_connect_to_client(SilcClient client,
633 SilcClientConnectionParams *params,
634 SilcPublicKey public_key,
635 SilcPrivateKey private_key,
636 char *remote_host, int port,
637 SilcClientConnectCallback callback,
640 SilcClientConnection conn;
642 if (!client || !remote_host)
645 /* Add new connection */
646 conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
647 public_key, private_key, remote_host,
648 port, callback, context);
650 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
654 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
655 "Connecting to port %d of client host %s",
658 /* Signal connection machine to start connecting */
659 conn->internal->connect = TRUE;
663 /* Starts key exchange in the remote stream indicated by `stream'. This
664 creates the connection context and returns it in the connection callback. */
666 SilcBool silc_client_key_exchange(SilcClient client,
667 SilcClientConnectionParams *params,
668 SilcPublicKey public_key,
669 SilcPrivateKey private_key,
671 SilcConnectionType conn_type,
672 SilcClientConnectCallback callback,
675 SilcClientConnection conn;
679 if (!client || !stream)
682 if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port))
685 /* Add new connection */
686 conn = silc_client_add_connection(client, conn_type, params,
687 public_key, private_key,
688 (char *)host, port, callback, context);
690 callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
693 conn->stream = (void *)stream;
695 /* Signal connection to start key exchange */
696 conn->internal->key_exchange = TRUE;
701 /* Finalizes the connection to the remote SILC server. This is called
702 after authentication protocol has been completed. This send our
703 user information to the server to receive our client ID from
706 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
708 SilcProtocol protocol = (SilcProtocol)context;
709 SilcClientConnAuthInternalContext *ctx =
710 (SilcClientConnAuthInternalContext *)protocol->context;
711 SilcClient client = (SilcClient)ctx->client;
712 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
715 SILC_LOG_DEBUG(("Start"));
717 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
718 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
719 /* Error occured during protocol */
720 SILC_LOG_DEBUG(("Error during authentication protocol"));
721 ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
725 if (conn->internal->params.detach_data) {
726 /* Send RESUME_CLIENT packet to the server, which is used to resume
727 old detached session back. */
729 SilcClientID *old_client_id;
730 unsigned char *old_id;
731 SilcUInt16 old_id_len;
733 if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
734 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
738 old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
739 if (!old_client_id) {
741 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
745 /* Generate authentication data that server will verify */
746 auth = silc_auth_public_key_auth_generate(client->public_key,
749 conn->internal->hash,
750 old_client_id, SILC_ID_CLIENT);
752 silc_free(old_client_id);
754 ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
758 packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
759 silc_buffer_format(packet,
760 SILC_STR_UI_SHORT(old_id_len),
761 SILC_STR_UI_XNSTRING(old_id, old_id_len),
762 SILC_STR_UI_XNSTRING(auth->data, auth->len),
765 /* Send the packet */
766 silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
768 packet->data, packet->len, TRUE);
769 silc_buffer_free(packet);
770 silc_buffer_free(auth);
771 silc_free(old_client_id);
774 /* Send NEW_CLIENT packet to the server. We will become registered
775 to the SILC network after sending this packet and we will receive
776 client ID from the server. */
777 packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
778 strlen(client->realname));
779 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
780 silc_buffer_format(packet,
781 SILC_STR_UI_SHORT(strlen(client->username)),
782 SILC_STR_UI_XNSTRING(client->username,
783 strlen(client->username)),
784 SILC_STR_UI_SHORT(strlen(client->realname)),
785 SILC_STR_UI_XNSTRING(client->realname,
786 strlen(client->realname)),
789 /* Send the packet */
790 silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
792 packet->data, packet->len, TRUE);
793 silc_buffer_free(packet);
796 /* Save remote ID. */
797 conn->remote_id = ctx->dest_id;
798 conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
799 conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
801 /* Register re-key timeout */
802 conn->internal->rekey->timeout = client->internal->params->rekey_secs;
803 conn->internal->rekey->context = (void *)client;
804 silc_schedule_task_add(client->schedule, conn->sock->sock,
805 silc_client_rekey_callback,
806 (void *)conn->sock, conn->internal->rekey->timeout, 0,
807 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
809 silc_protocol_free(protocol);
810 silc_free(ctx->auth_data);
812 silc_ske_free(ctx->ske);
813 silc_socket_free(ctx->sock);
815 conn->sock->protocol = NULL;
819 silc_protocol_free(protocol);
820 silc_free(ctx->auth_data);
821 silc_free(ctx->dest_id);
823 silc_ske_free(ctx->ske);
824 conn->sock->protocol = NULL;
825 silc_socket_free(ctx->sock);
827 /* Notify application of failure */
828 silc_schedule_task_add(client->schedule, ctx->sock->sock,
829 silc_client_connect_failure_auth, ctx,
830 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
833 /* Client session resuming callback. If the session was resumed
834 this callback is called after the resuming is completed. This
835 will call the `connect' client operation to the application
836 since it has not been called yet. */
838 static void silc_client_resume_session_cb(SilcClient client,
839 SilcClientConnection conn,
845 /* Notify application that connection is created to server */
846 client->internal->ops->connected(client, conn, success ?
847 SILC_CLIENT_CONN_SUCCESS_RESUME :
848 SILC_CLIENT_CONN_ERROR_RESUME);
851 /* Issue INFO command to fetch the real server name and server
852 information and other stuff. */
853 silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
854 silc_client_command_reply_info_i, 0,
856 sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
857 silc_client_command_send(client, conn, SILC_COMMAND_INFO,
858 conn->cmd_ident, 1, 2, sidp->data, sidp->len);
859 silc_buffer_free(sidp);
863 /* Processes incoming connection authentication method request packet.
864 It is a reply to our previously sent request. The packet can be used
865 to resolve the authentication method for the current session if the
866 client does not know it beforehand. */
868 void silc_client_connection_auth_request(SilcClient client,
869 SilcClientConnection conn,
870 SilcPacketContext *packet)
872 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
873 SilcUInt16 conn_type, auth_meth;
876 /* If we haven't send our request then ignore this one. */
877 if (!conn->internal->connauth)
880 /* Parse the payload */
881 ret = silc_buffer_unformat(packet->buffer,
882 SILC_STR_UI_SHORT(&conn_type),
883 SILC_STR_UI_SHORT(&auth_meth),
886 auth_meth = SILC_AUTH_NONE;
888 /* Call the request callback to notify application for received
889 authentication method information. */
890 if (conn->internal->connauth->callback)
891 (*conn->internal->connauth->callback)(client, conn, auth_meth,
892 conn->internal->connauth->context);
894 silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
896 silc_free(conn->internal->connauth);
897 conn->internal->connauth = NULL;
900 /* Timeout task callback called if the server does not reply to our
901 connection authentication method request in the specified time interval. */
903 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
905 SilcClientConnection conn = (SilcClientConnection)context;
906 SilcClient client = conn->client;
908 if (!conn->internal->connauth)
911 /* Call the request callback to notify application */
912 if (conn->internal->connauth->callback)
913 (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
914 conn->internal->connauth->context);
916 silc_free(conn->internal->connauth);
917 conn->internal->connauth = NULL;
920 /* This function can be used to request the current authentication method
921 from the server. This may be called when connecting to the server
922 and the client library requests the authentication data from the
923 application. If the application does not know the current authentication
924 method it can request it from the server using this function.
925 The `callback' with `context' will be called after the server has
926 replied back with the current authentication method. */
929 silc_client_request_authentication_method(SilcClient client,
930 SilcClientConnection conn,
931 SilcConnectionAuthRequest callback,
934 SilcClientConnAuthRequest connauth;
937 assert(client && conn);
938 connauth = silc_calloc(1, sizeof(*connauth));
939 connauth->callback = callback;
940 connauth->context = context;
942 if (conn->internal->connauth)
943 silc_free(conn->internal->connauth);
945 conn->internal->connauth = connauth;
947 /* Assemble the request packet and send it to the server */
948 packet = silc_buffer_alloc(4);
949 silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
950 silc_buffer_format(packet,
951 SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
952 SILC_STR_UI_SHORT(SILC_AUTH_NONE),
954 silc_client_packet_send(client, conn->sock,
955 SILC_PACKET_CONNECTION_AUTH_REQUEST,
957 packet->data, packet->len, FALSE);
958 silc_buffer_free(packet);
960 /* Register a timeout in case server does not reply anything back. */
962 silc_schedule_task_add(client->schedule, conn->sock->sock,
963 silc_client_request_authentication_method_timeout,
965 client->internal->params->connauth_request_secs, 0,
966 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
971 /* Allocates new client object. This has to be done before client may
972 work. After calling this one must call silc_client_init to initialize
973 the client. The `application' is application specific user data pointer
974 and caller must free it. */
976 SilcClient silc_client_alloc(SilcClientOperations *ops,
977 SilcClientParams *params,
979 const char *version_string)
981 SilcClient new_client;
983 new_client = silc_calloc(1, sizeof(*new_client));
986 new_client->application = application;
988 new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
989 if (!new_client->internal) {
990 silc_free(new_client);
993 new_client->internal->ops = ops;
994 new_client->internal->params =
995 silc_calloc(1, sizeof(*new_client->internal->params));
997 version_string = silc_version_string;
998 new_client->internal->silc_client_version = strdup(version_string);
1001 memcpy(new_client->internal->params, params, sizeof(*params));
1003 if (!new_client->internal->params->task_max)
1004 new_client->internal->params->task_max = 200;
1006 if (!new_client->internal->params->rekey_secs)
1007 new_client->internal->params->rekey_secs = 3600;
1009 if (!new_client->internal->params->connauth_request_secs)
1010 new_client->internal->params->connauth_request_secs = 2;
1012 new_client->internal->params->
1013 nickname_format[sizeof(new_client->internal->
1014 params->nickname_format) - 1] = 0;
1019 /* Frees client object and its internals. */
1021 void silc_client_free(SilcClient client)
1025 silc_rng_free(client->rng);
1027 if (!client->internal->params->dont_register_crypto_library) {
1028 silc_cipher_unregister_all();
1029 silc_pkcs_unregister_all();
1030 silc_hash_unregister_all();
1031 silc_hmac_unregister_all();
1034 silc_hash_free(client->md5hash);
1035 silc_hash_free(client->sha1hash);
1036 silc_hmac_free(client->internal->md5hmac);
1037 silc_hmac_free(client->internal->sha1hmac);
1038 silc_free(client->internal->params);
1039 silc_free(client->internal->silc_client_version);
1040 silc_free(client->internal);
1045 /* Initializes the client. This makes all the necessary steps to make
1046 the client ready to be run. One must call silc_client_run to run the
1047 client. Returns FALSE if error occured, TRUE otherwise. */
1049 SilcBool silc_client_init(SilcClient client)
1051 SILC_LOG_DEBUG(("Initializing client"));
1054 assert(client->username);
1055 assert(client->hostname);
1056 assert(client->realname);
1058 /* Validate essential strings */
1059 if (client->nickname)
1060 if (!silc_identifier_verify(client->nickname, strlen(client->nickname),
1061 SILC_STRING_UTF8, 128)) {
1062 SILC_LOG_ERROR(("Malformed nickname '%s'", client->nickname));
1065 if (!silc_identifier_verify(client->username, strlen(client->username),
1066 SILC_STRING_UTF8, 128)) {
1067 SILC_LOG_ERROR(("Malformed username '%s'", client->username));
1070 if (!silc_identifier_verify(client->hostname, strlen(client->hostname),
1071 SILC_STRING_UTF8, 256)) {
1072 SILC_LOG_ERROR(("Malformed hostname '%s'", client->hostname));
1075 if (!silc_utf8_valid(client->realname, strlen(client->realname))) {
1076 SILC_LOG_ERROR(("Malformed realname '%s'", client->realname));
1080 if (!client->internal->params->dont_register_crypto_library) {
1081 /* Initialize the crypto library. If application has done this already
1082 this has no effect. Also, we will not be overriding something
1083 application might have registered earlier. */
1084 silc_cipher_register_default();
1085 silc_pkcs_register_default();
1086 silc_hash_register_default();
1087 silc_hmac_register_default();
1090 /* Initialize hash functions for client to use */
1091 silc_hash_alloc("md5", &client->md5hash);
1092 silc_hash_alloc("sha1", &client->sha1hash);
1094 /* Initialize random number generator */
1095 client->rng = silc_rng_alloc();
1096 silc_rng_init(client->rng);
1097 silc_rng_global_init(client->rng);
1099 /* Initialize the scheduler */
1101 silc_schedule_init(client->internal->params->task_max ?
1102 client->internal->params->task_max : 200, client);
1103 if (!client->schedule)
1106 /* Start packet engine */
1107 client->internal->packet_engine =
1108 silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1110 if (!client->internal->packet_engine)
1113 /* Initialize FSM */
1114 if (!silc_fsm_init(&client->internal->fsm, client, NULL, NULL,
1117 silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1119 /* Allocate client lock */
1120 silc_mutex_alloc(&client->internal->lock);
1122 /* Register commands */
1123 silc_client_commands_register(client);
1128 /* Stops the client. This is called to stop the client and thus to stop
1131 void silc_client_stop(SilcClient client)
1133 SILC_LOG_DEBUG(("Stopping client"));
1135 silc_schedule_stop(client->schedule);
1136 silc_schedule_uninit(client->schedule);
1138 silc_client_commands_unregister(client);
1140 SILC_LOG_DEBUG(("Client stopped"));
1143 /* Starts the SILC client FSM machine and blocks here. When this returns
1144 the client has ended. */
1146 void silc_client_run(SilcClient client)
1148 SILC_LOG_DEBUG(("Starting SILC client"));
1150 /* Start the client */
1151 silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1153 /* Signal the application when we are running */
1154 client->internal->run_callback = TRUE;
1155 SILC_FSM_SEMA_POST(&client->internal->wait_event);
1157 /* Run the scheduler */
1158 silc_schedule(client->schedule);
1161 /* Call scheduler one iteration and return. This cannot be called if threads
1164 void silc_client_run_one(SilcClient client)
1166 silc_schedule_one(client->schedule, -1);