Added connection authentication request support.
[silc.git] / lib / silcclient / client.c
1 /*
2
3   client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8
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.
12
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.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Types and definitions ***************************/
26
27
28 /************************ Static utility functions **************************/
29
30 /* Connection machine FSM destructor.  This will finish the thread where
31    the machine was running and deletes the connection context. */
32
33 static void silc_client_connection_destructor(SilcFSM fsm,
34                                               void *fsm_context,
35                                               void *destructor_context)
36 {
37   SilcClientConnection conn = fsm_context;
38   SilcFSMThread thread = destructor_context;
39
40   SILC_LOG_DEBUG(("Connection %p finished", conn));
41
42   /* Delete connection */
43   silc_client_del_connection(conn->client, conn);
44
45   /* Finish the thread were this machine was running */
46   silc_fsm_finish(thread);
47 }
48
49 /* Connection thread FSM destructor.  This was the thread where the connection
50    machine was running (may be real thread).  From here we notify client
51    that the connection thread has finished. */
52
53 static void silc_client_connection_finished(SilcFSMThread fsm,
54                                             void *fsm_context,
55                                             void *destructor_context)
56 {
57   SilcClient client = silc_fsm_get_state_context(fsm);
58
59   /* Signal client that we have finished */
60   silc_atomic_sub_int16(&client->internal->conns, 1);
61   client->internal->connection_closed = TRUE;
62   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
63
64   silc_fsm_free(fsm);
65 }
66
67 /* Packet FSM thread destructor */
68
69 static void silc_client_packet_destructor(SilcFSMThread thread,
70                                           void *thread_context,
71                                           void *destructor_context)
72 {
73   SilcClientConnection conn = thread_context;
74
75   /* Add thread back to thread pool */
76   silc_list_add(conn->internal->thread_pool, thread);
77   if (silc_list_count(conn->internal->thread_pool) == 1)
78     silc_list_start(conn->internal->thread_pool);
79 }
80
81 /* Packet engine callback to receive a packet */
82
83 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
84                                            SilcPacketStream stream,
85                                            SilcPacket packet,
86                                            void *callback_context,
87                                            void *stream_context)
88 {
89   SilcClientConnection conn = stream_context;
90   SilcFSMThread thread;
91
92   /* Packets we do not handle */
93   switch (packet->type) {
94   case SILC_PACKET_HEARTBEAT:
95   case SILC_PACKET_SUCCESS:
96   case SILC_PACKET_FAILURE:
97   case SILC_PACKET_REJECT:
98   case SILC_PACKET_KEY_EXCHANGE:
99   case SILC_PACKET_KEY_EXCHANGE_1:
100   case SILC_PACKET_KEY_EXCHANGE_2:
101   case SILC_PACKET_REKEY_DONE:
102   case SILC_PACKET_CONNECTION_AUTH:
103     return FALSE;
104     break;
105   }
106
107   /* Get packet processing thread */
108   thread = silc_list_get(conn->internal->thread_pool);
109   if (!thread) {
110     thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
111                                    silc_client_packet_destructor, NULL, FALSE);
112     if (!thread)
113       return FALSE;
114   } else {
115     silc_list_del(conn->internal->thread_pool, thread);
116     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
117                          silc_client_packet_destructor, NULL, FALSE);
118   }
119
120   /* Process packet in thread */
121   silc_fsm_set_state_context(thread, packet);
122   silc_fsm_start_sync(thread, silc_client_connection_st_packet);
123
124   return TRUE;
125 }
126
127 /* Packet engine callback to indicate end of stream */
128
129 static void silc_client_packet_eos(SilcPacketEngine engine,
130                                    SilcPacketStream stream,
131                                    void *callback_context,
132                                    void *stream_context)
133 {
134   SilcClientConnection conn = stream_context;
135   SilcClient client = conn->client;
136
137   SILC_LOG_DEBUG(("Remote disconnected connection"));
138
139   /* Call connection callback */
140   if (!conn->internal->callback_called)
141     conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, 0, NULL,
142                    conn->callback_context);
143   conn->internal->callback_called = TRUE;
144
145   /* Signal to close connection */
146   if (!conn->internal->disconnected) {
147     conn->internal->disconnected = TRUE;
148     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
149   }
150 }
151
152 /* Packet engine callback to indicate error */
153
154 static void silc_client_packet_error(SilcPacketEngine engine,
155                                      SilcPacketStream stream,
156                                      SilcPacketError error,
157                                      void *callback_context,
158                                      void *stream_context)
159 {
160   /* Nothing */
161 }
162
163 /* Packet stream callbacks */
164 static SilcPacketCallbacks silc_client_stream_cbs =
165 {
166   silc_client_packet_receive,
167   silc_client_packet_eos,
168   silc_client_packet_error
169 };
170
171 /* FSM destructor */
172
173 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
174                                 void *destructor_context)
175 {
176   silc_fsm_free(fsm);
177 }
178
179 /* Connect abort operation */
180
181 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
182 {
183   SilcClientConnection conn = context;
184
185   SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
186
187   /* Signal to close connection */
188   if (!conn->internal->disconnected) {
189     conn->internal->disconnected = TRUE;
190     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
191   }
192 }
193
194 /************************** Connection's machine ****************************/
195
196 /* Start the connection's state machine.  If threads are in use the machine
197    is always executed in a real thread. */
198
199 SILC_FSM_STATE(silc_client_connection_st_start)
200 {
201   SilcClientConnection conn = fsm_context;
202   SilcFSM connfsm;
203
204   /* Take scheduler for connection */
205   conn->internal->schedule = silc_fsm_get_schedule(fsm);
206
207   /*** Run connection machine */
208   connfsm = &conn->internal->fsm;
209   silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
210                 fsm, conn->internal->schedule);
211   silc_fsm_event_init(&conn->internal->wait_event, connfsm);
212   silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
213
214   /* Schedule any events set in initialization */
215   if (conn->internal->connect)
216     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
217   if (conn->internal->key_exchange)
218     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
219
220   /* Wait until this thread is terminated from the machine destructor */
221   SILC_FSM_WAIT;
222 }
223
224 /* Connection machine main state.  This handles various connection related
225    events, but not packet processing.  It's done in dedicated packet
226    processing FSM thread. */
227
228 SILC_FSM_STATE(silc_client_connection_st_run)
229 {
230   SilcClientConnection conn = fsm_context;
231   SilcFSMThread thread;
232
233   /* Wait for events */
234   SILC_FSM_EVENT_WAIT(&conn->internal->wait_event);
235
236   /* Process events */
237   thread = &conn->internal->event_thread;
238
239   if (conn->internal->connect) {
240     SILC_LOG_DEBUG(("Event: connect"));
241     conn->internal->connect = FALSE;
242
243     /*** Event: connect */
244     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
245                          NULL, NULL, FALSE);
246     silc_fsm_start_sync(thread, silc_client_st_connect);
247     SILC_FSM_CONTINUE;
248   }
249
250   if (conn->internal->key_exchange) {
251     SILC_LOG_DEBUG(("Event: key exchange"));
252     conn->internal->key_exchange = FALSE;
253
254     /*** Event: key exchange */
255     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
256                          NULL, NULL, FALSE);
257     silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
258     SILC_FSM_CONTINUE;
259   }
260
261   if (conn->internal->rekeying) {
262     SILC_LOG_DEBUG(("Event: rekey"));
263     conn->internal->rekeying = FALSE;
264
265     /*** Event: rekey */
266     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
267                          NULL, NULL, FALSE);
268     silc_fsm_start_sync(thread, silc_client_st_rekey);
269     SILC_FSM_CONTINUE;
270   }
271
272   if (conn->internal->disconnected) {
273     /** Event: disconnected */
274     SILC_LOG_DEBUG(("Event: disconnected"));
275     silc_fsm_next(fsm, silc_client_connection_st_close);
276     SILC_FSM_YIELD;
277   }
278
279   /* NOT REACHED */
280   SILC_ASSERT(FALSE);
281   SILC_FSM_CONTINUE;
282 }
283
284 /* Packet processor thread.  Each incoming packet is processed in FSM
285    thread in this state.  The thread is run in the connection machine. */
286
287 SILC_FSM_STATE(silc_client_connection_st_packet)
288 {
289   SilcClientConnection conn = fsm_context;
290   SilcPacket packet = state_context;
291
292   SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
293
294   switch (packet->type) {
295
296   case SILC_PACKET_PRIVATE_MESSAGE:
297     /** Private message */
298     silc_fsm_next(fsm, silc_client_private_message);
299     break;
300
301   case SILC_PACKET_CHANNEL_MESSAGE:
302     /** Channel message */
303     silc_fsm_next(fsm, silc_client_channel_message);
304     break;
305
306   case SILC_PACKET_FTP:
307     /* File transfer packet */
308 //    silc_fsm_next(fsm, silc_client_ftp);
309     break;
310
311   case SILC_PACKET_CHANNEL_KEY:
312     /** Channel key */
313     silc_fsm_next(fsm, silc_client_channel_key);
314     break;
315
316   case SILC_PACKET_COMMAND_REPLY:
317     /** Command reply */
318     silc_fsm_next(fsm, silc_client_command_reply);
319     break;
320
321   case SILC_PACKET_NOTIFY:
322     /** Notify */
323     silc_fsm_next(fsm, silc_client_notify);
324     break;
325
326   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
327     /* Private message key indicator */
328     silc_fsm_next(fsm, silc_client_private_message_key);
329     break;
330
331   case SILC_PACKET_DISCONNECT:
332     /** Disconnect */
333     silc_fsm_next(fsm, silc_client_disconnect);
334     break;
335
336   case SILC_PACKET_ERROR:
337     /* Error by server */
338     silc_fsm_next(fsm, silc_client_error);
339     break;
340
341   case SILC_PACKET_KEY_AGREEMENT:
342     /** Key agreement */
343     silc_fsm_next(fsm, silc_client_key_agreement);
344     break;
345
346   case SILC_PACKET_COMMAND:
347     /** Command packet */
348     silc_fsm_next(fsm, silc_client_command);
349     break;
350
351   case SILC_PACKET_NEW_ID:
352     /** New ID */
353     silc_fsm_next(fsm, silc_client_new_id);
354     break;
355
356   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
357     /** Connection auth resolve reply */
358     silc_fsm_next(fsm, silc_client_connect_auth_request);
359     break;
360
361   case SILC_PACKET_REKEY:
362     /* Signal to start rekey */
363     conn->internal->rekey_responder = TRUE;
364     conn->internal->rekeying = TRUE;
365     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
366
367     silc_packet_free(packet);
368     SILC_FSM_FINISH;
369     break;
370
371   default:
372     silc_packet_free(packet);
373     SILC_FSM_FINISH;
374     break;
375   }
376
377   SILC_FSM_CONTINUE;
378 }
379
380 /* Disconnection event to close remote connection.  We close the connection
381    and finish the connection machine in this state.  The connection context
382    is deleted in the machine destructor.  The connection callback must be
383    already called back to application before getting here. */
384
385 SILC_FSM_STATE(silc_client_connection_st_close)
386 {
387   SilcClientConnection conn = fsm_context;
388   SilcClientCommandContext cmd;
389
390   /* Finish running command threads.  This will also finish waiting packet
391      thread, as they are always waiting for some command.  If any thread is
392      waiting something else than command, they must be finished explicitly. */
393   if (silc_list_count(conn->internal->pending_commands)) {
394     SILC_LOG_DEBUG(("Finish pending commands"));
395     silc_list_start(conn->internal->pending_commands);
396     while ((cmd = silc_list_get(conn->internal->pending_commands))) {
397       if (silc_fsm_is_started(&cmd->thread)) {
398         cmd->verbose = FALSE;
399         silc_fsm_continue_sync(&cmd->thread);
400       }
401     }
402
403     /* Give threads time to finish */
404     SILC_FSM_YIELD;
405   }
406
407   /* Abort ongoing event */
408   if (conn->internal->op) {
409     SILC_LOG_DEBUG(("Abort event"));
410     silc_async_abort(conn->internal->op, NULL, NULL);
411     conn->internal->op = NULL;
412   }
413
414   /* If event thread is running, finish it. */
415   if (silc_fsm_is_started(&conn->internal->event_thread)) {
416     SILC_LOG_DEBUG(("Finish event thread"));
417     silc_fsm_continue_sync(&conn->internal->event_thread);
418     SILC_FSM_YIELD;
419   }
420
421   SILC_LOG_DEBUG(("Closing remote connection"));
422
423   /* Close connection */
424   silc_packet_stream_destroy(conn->stream);
425
426   SILC_LOG_DEBUG(("Finishing connection machine"));
427   SILC_FSM_FINISH;
428 }
429
430 /* Received error packet from server.  Send it to application. */
431
432 SILC_FSM_STATE(silc_client_error)
433 {
434   SilcClientConnection conn = fsm_context;
435   SilcClient client = conn->client;
436   SilcPacket packet = state_context;
437   char *msg;
438
439   msg = silc_memdup(silc_buffer_data(&packet->buffer),
440                     silc_buffer_len(&packet->buffer));
441   if (msg)
442     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
443
444   silc_free(msg);
445   silc_packet_free(packet);
446
447   SILC_FSM_FINISH;
448 }
449
450 /* Received disconnect packet from server.  We close the connection and
451    send the disconnect message to application. */
452
453 SILC_FSM_STATE(silc_client_disconnect)
454 {
455   SilcClientConnection conn = fsm_context;
456   SilcClient client = conn->client;
457   SilcPacket packet = state_context;
458   SilcStatus status;
459   char *message = NULL;
460
461   SILC_LOG_DEBUG(("Server disconnected"));
462
463   if (silc_buffer_len(&packet->buffer) < 1) {
464     silc_packet_free(packet);
465     SILC_FSM_FINISH;
466   }
467
468   status = (SilcStatus)packet->buffer.data[0];
469
470   silc_buffer_pull(&packet->buffer, 1);
471   if (silc_buffer_len(&packet->buffer) > 1 &&
472       silc_utf8_valid(silc_buffer_data(&packet->buffer),
473                       silc_buffer_len(&packet->buffer)))
474     message = silc_memdup(silc_buffer_data(&packet->buffer),
475                           silc_buffer_len(&packet->buffer));
476
477   /* Call connection callback */
478   if (!conn->internal->callback_called)
479     conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
480                    message, conn->callback_context);
481   conn->internal->callback_called = TRUE;
482
483   silc_free(message);
484   silc_packet_free(packet);
485
486   /* Signal to close connection */
487   if (!conn->internal->disconnected) {
488     conn->internal->disconnected = TRUE;
489     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
490   }
491
492   SILC_FSM_FINISH;
493 }
494
495 /*************************** Main client machine ****************************/
496
497 /* The client's main state where we wait for various events */
498
499 SILC_FSM_STATE(silc_client_st_run)
500 {
501   SilcClient client = fsm_context;
502
503   /* Wait for events */
504   SILC_FSM_EVENT_WAIT(&client->internal->wait_event);
505
506   /* Process events */
507
508   if (client->internal->run_callback && client->internal->running) {
509     /* Call running callbcak back to application */
510     SILC_LOG_DEBUG(("We are up, call running callback"));
511     client->internal->run_callback = FALSE;
512     client->internal->running(client, client->internal->running_context);
513     SILC_FSM_CONTINUE;
514   }
515
516   if (client->internal->connection_closed) {
517     /* A connection finished */
518     SILC_LOG_DEBUG(("Event: connection closed"));
519     client->internal->connection_closed = FALSE;
520     if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
521         client->internal->stop)
522       SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
523     SILC_FSM_CONTINUE;
524   }
525
526   if (client->internal->stop) {
527     /* Stop client libarry.  If we have running connections, wait until
528        they finish first. */
529     SILC_LOG_DEBUG(("Event: stop"));
530     if (silc_atomic_get_int16(&client->internal->conns) == 0)
531       silc_fsm_next(fsm, silc_client_st_stop);
532     SILC_FSM_CONTINUE;
533   }
534
535   /* NOT REACHED */
536   SILC_ASSERT(FALSE);
537   SILC_FSM_CONTINUE;
538 }
539
540 /* Stop event.  Stops the client library. */
541
542 SILC_FSM_STATE(silc_client_st_stop)
543 {
544   SilcClient client = fsm_context;
545
546   SILC_LOG_DEBUG(("Client stopped"));
547
548   /* Stop scheduler */
549   silc_schedule_stop(client->schedule);
550   silc_client_commands_unregister(client);
551
552   /* Call stopped callback to application */
553   if (client->internal->running)
554     client->internal->running(client, client->internal->running_context);
555
556   SILC_FSM_FINISH;
557 }
558
559 /******************************* Private API ********************************/
560
561 /* Adds new connection.  Creates the connection context and returns it. */
562
563 static SilcClientConnection
564 silc_client_add_connection(SilcClient client,
565                            SilcConnectionType conn_type,
566                            SilcClientConnectionParams *params,
567                            SilcPublicKey public_key,
568                            SilcPrivateKey private_key,
569                            char *remote_host, int port,
570                            SilcClientConnectCallback callback,
571                            void *context)
572 {
573   SilcClientConnection conn;
574   SilcFSMThread thread;
575
576   if (!callback)
577     return NULL;
578
579   SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
580
581   conn = silc_calloc(1, sizeof(*conn));
582   if (!conn)
583     return NULL;
584
585   conn->client = client;
586   conn->public_key = public_key;
587   conn->private_key = private_key;
588   conn->remote_host = strdup(remote_host);
589   conn->remote_port = port ? port : 706;
590   conn->type = conn_type;
591   conn->callback = callback;
592   conn->callback_context = context;
593
594   conn->internal = silc_calloc(1, sizeof(*conn->internal));
595   if (!conn->internal) {
596     silc_free(conn);
597     return NULL;
598   }
599   conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
600   silc_mutex_alloc(&conn->internal->lock);
601   silc_atomic_init16(&conn->internal->cmd_ident, 0);
602
603   if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
604     silc_free(conn);
605     silc_free(conn->internal);
606     return NULL;
607   }
608
609   /* Set parameters */
610   if (params)
611     conn->internal->params = *params;
612   if (!conn->internal->params.rekey_secs)
613     conn->internal->params.rekey_secs = 3600;
614 #ifndef SILC_DIST_INPLACE
615   if (conn->internal->params.rekey_secs < 300)
616     conn->internal->params.rekey_secs = 300;
617 #endif /* SILC_DIST_INPLACE */
618
619   conn->internal->verbose = TRUE;
620   silc_list_init(conn->internal->pending_commands,
621                  struct SilcClientCommandContextStruct, next);
622   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
623
624   /* Allocate client, channel and serve caches */
625   conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
626                                                     NULL, NULL);
627   conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
628                                                      NULL, NULL);
629   conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
630                                                     NULL, NULL);
631   if (!conn->internal->client_cache || !conn->internal->channel_cache ||
632       !conn->internal->server_cache) {
633     silc_client_del_connection(client, conn);
634     return NULL;
635   }
636
637   conn->internal->ftp_sessions = silc_dlist_init();
638
639   /* Initialize our async operation so that application may abort us
640      while we're connecting. */
641   conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
642                                          NULL, conn);
643   if (!conn->internal->cop) {
644     silc_client_del_connection(client, conn);
645     return NULL;
646   }
647
648   /* Run the connection state machine.  If threads are in use the machine
649      is always run in a real thread. */
650   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
651                                  silc_client_connection_finished, NULL,
652                                  client->internal->params->threads);
653   if (!thread) {
654     silc_client_del_connection(client, conn);
655     return NULL;
656   }
657   silc_fsm_set_state_context(thread, client);
658   silc_fsm_start(thread, silc_client_connection_st_start);
659
660   SILC_LOG_DEBUG(("New connection %p", conn));
661   silc_atomic_add_int16(&client->internal->conns, 1);
662
663   return conn;
664 }
665
666 /* Deletes connection.  This is always called from the connection machine
667    destructor.  Do not call this directly other places. */
668
669 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
670 {
671   SilcList list;
672   SilcIDCacheEntry entry;
673   SilcFSMThread thread;
674
675   SILC_LOG_DEBUG(("Freeing connection %p", conn));
676
677   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
678
679   /* Free all cache entries */
680   if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
681     silc_list_start(list);
682     while ((entry = silc_list_get(list)))
683       silc_client_del_server(client, conn, entry->context);
684   }
685   if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
686     silc_list_start(list);
687     while ((entry = silc_list_get(list))) {
688       silc_client_empty_channel(client, conn, entry->context);
689       silc_client_del_channel(client, conn, entry->context);
690     }
691   }
692   if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
693     silc_list_start(list);
694     while ((entry = silc_list_get(list)))
695       silc_client_del_client(client, conn, entry->context);
696   }
697
698   /* Free ID caches */
699   if (conn->internal->client_cache)
700     silc_idcache_free(conn->internal->client_cache);
701   if (conn->internal->channel_cache)
702     silc_idcache_free(conn->internal->channel_cache);
703   if (conn->internal->server_cache)
704     silc_idcache_free(conn->internal->server_cache);
705
706   /* Free thread pool */
707   silc_list_start(conn->internal->thread_pool);
708   while ((thread = silc_list_get(conn->internal->thread_pool)))
709     silc_fsm_free(thread);
710
711   silc_free(conn->remote_host);
712   silc_buffer_free(conn->internal->local_idp);
713   silc_buffer_free(conn->internal->remote_idp);
714   silc_mutex_free(conn->internal->lock);
715   if (conn->internal->hash)
716     silc_hash_free(conn->internal->hash);
717   if (conn->internal->sha1hash)
718     silc_hash_free(conn->internal->sha1hash);
719   silc_atomic_uninit16(&conn->internal->cmd_ident);
720
721   silc_free(conn->internal);
722   memset(conn, 'F', sizeof(*conn));
723   silc_free(conn);
724 }
725
726 /******************************* Client API *********************************/
727
728 /* Connects to remote server.  This is the main routine used to connect
729    to remote SILC server.  Performs key exchange also.  Returns the
730    connection context to the connection callback. */
731
732 SilcAsyncOperation
733 silc_client_connect_to_server(SilcClient client,
734                               SilcClientConnectionParams *params,
735                               SilcPublicKey public_key,
736                               SilcPrivateKey private_key,
737                               char *remote_host, int port,
738                               SilcClientConnectCallback callback,
739                               void *context)
740 {
741   SilcClientConnection conn;
742
743   SILC_LOG_DEBUG(("Connecting to server"));
744
745   if (!client || !remote_host)
746     return NULL;
747
748   /* Add new connection */
749   conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
750                                     public_key, private_key, remote_host,
751                                     port, callback, context);
752   if (!conn) {
753     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
754     return NULL;
755   }
756
757   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
758                              "Connecting to port %d of server %s",
759                              port, remote_host);
760
761   /* Signal connection machine to start connecting */
762   conn->internal->connect = TRUE;
763   return conn->internal->cop;
764 }
765
766 /* Connects to remote client.  Performs key exchange also.  Returns the
767    connection context to the connection callback. */
768
769 SilcAsyncOperation
770 silc_client_connect_to_client(SilcClient client,
771                               SilcClientConnectionParams *params,
772                               SilcPublicKey public_key,
773                               SilcPrivateKey private_key,
774                               char *remote_host, int port,
775                               SilcClientConnectCallback callback,
776                               void *context)
777 {
778   SilcClientConnection conn;
779
780   SILC_LOG_DEBUG(("Connecting to client"));
781
782   if (!client || !remote_host)
783     return NULL;
784
785   /* Add new connection */
786   conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
787                                     public_key, private_key, remote_host,
788                                     port, callback, context);
789   if (!conn) {
790     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
791     return NULL;
792   }
793
794   /* Signal connection machine to start connecting */
795   conn->internal->connect = TRUE;
796   return conn->internal->cop;
797 }
798
799 /* Starts key exchange in the remote stream indicated by `stream'.  This
800    creates the connection context and returns it in the connection callback. */
801
802 SilcAsyncOperation
803 silc_client_key_exchange(SilcClient client,
804                          SilcClientConnectionParams *params,
805                          SilcPublicKey public_key,
806                          SilcPrivateKey private_key,
807                          SilcStream stream,
808                          SilcConnectionType conn_type,
809                          SilcClientConnectCallback callback,
810                          void *context)
811 {
812   SilcClientConnection conn;
813   const char *host;
814   SilcUInt16 port;
815
816   SILC_LOG_DEBUG(("Performing key exchange"));
817
818   if (!client || !stream)
819     return NULL;
820
821   if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
822     SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
823     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
824     return NULL;
825   }
826
827   /* Add new connection */
828   conn = silc_client_add_connection(client, conn_type, params,
829                                     public_key, private_key,
830                                     (char *)host, port, callback, context);
831   if (!conn) {
832     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
833     return NULL;
834   }
835   conn->stream = (void *)stream;
836
837   /* Signal connection to start key exchange */
838   conn->internal->key_exchange = TRUE;
839   return conn->internal->cop;
840 }
841
842 /* Closes remote connection */
843
844 void silc_client_close_connection(SilcClient client,
845                                   SilcClientConnection conn)
846 {
847   SILC_LOG_DEBUG(("Closing connection %p", conn));
848
849   /* Signal to close connection */
850   if (!conn->internal->disconnected) {
851     conn->internal->disconnected = TRUE;
852     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
853   }
854 }
855
856 /* Allocates new client object. This has to be done before client may
857    work. After calling this one must call silc_client_init to initialize
858    the client. The `application' is application specific user data pointer
859    and caller must free it. */
860
861 SilcClient silc_client_alloc(SilcClientOperations *ops,
862                              SilcClientParams *params,
863                              void *application,
864                              const char *version_string)
865 {
866   SilcClient new_client;
867
868   new_client = silc_calloc(1, sizeof(*new_client));
869   if (!new_client)
870     return NULL;
871   new_client->application = application;
872
873   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
874   if (!new_client->internal) {
875     silc_free(new_client);
876     return NULL;
877   }
878   new_client->internal->ops = ops;
879   new_client->internal->params =
880     silc_calloc(1, sizeof(*new_client->internal->params));
881   if (!version_string)
882     version_string = silc_version_string;
883   new_client->internal->silc_client_version = strdup(version_string);
884
885   if (params)
886     memcpy(new_client->internal->params, params, sizeof(*params));
887
888   new_client->internal->params->
889     nickname_format[sizeof(new_client->internal->
890                            params->nickname_format) - 1] = 0;
891
892   silc_atomic_init16(&new_client->internal->conns, 0);
893
894   return new_client;
895 }
896
897 /* Frees client object and its internals. */
898
899 void silc_client_free(SilcClient client)
900 {
901   silc_schedule_uninit(client->schedule);
902
903   if (client->rng)
904     silc_rng_free(client->rng);
905
906   if (!client->internal->params->dont_register_crypto_library) {
907     silc_cipher_unregister_all();
908     silc_pkcs_unregister_all();
909     silc_hash_unregister_all();
910     silc_hmac_unregister_all();
911   }
912
913   silc_atomic_uninit16(&client->internal->conns);
914   silc_free(client->username);
915   silc_free(client->hostname);
916   silc_free(client->realname);
917   silc_free(client->internal->params);
918   silc_free(client->internal->silc_client_version);
919   silc_free(client->internal);
920   silc_free(client);
921 }
922
923 /* Initializes the client. This makes all the necessary steps to make
924    the client ready to be run. One must call silc_client_run to run the
925    client. Returns FALSE if error occured, TRUE otherwise. */
926
927 SilcBool silc_client_init(SilcClient client, const char *username,
928                           const char *hostname, const char *realname,
929                           SilcClientRunning running, void *context)
930 {
931   SILC_LOG_DEBUG(("Initializing client"));
932
933   if (!client)
934     return FALSE;
935
936   if (!username || !hostname) {
937     SILC_LOG_ERROR(("Username and hostname must be given to "
938                     "silc_client_init"));
939     return FALSE;
940   }
941   if (!realname)
942     realname = username;
943
944   /* Validate essential strings */
945   if (!silc_identifier_verify(username, strlen(username),
946                               SILC_STRING_UTF8, 128)) {
947     SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
948                     client->username));
949     return FALSE;
950   }
951   if (!silc_identifier_verify(hostname, strlen(hostname),
952                               SILC_STRING_UTF8, 256)) {
953     SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
954                     client->hostname));
955     return FALSE;
956   }
957   if (!silc_utf8_valid(realname, strlen(realname))) {
958     SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
959                     client->realname));
960     return FALSE;
961   }
962
963   /* Take the name strings */
964   client->username = strdup(username);
965   client->hostname = strdup(hostname);
966   client->realname = strdup(realname);
967   if (!username || !hostname || !realname)
968     return FALSE;
969
970   if (!client->internal->params->dont_register_crypto_library) {
971     /* Initialize the crypto library.  If application has done this already
972        this has no effect.  Also, we will not be overriding something
973        application might have registered earlier. */
974     silc_cipher_register_default();
975     silc_pkcs_register_default();
976     silc_hash_register_default();
977     silc_hmac_register_default();
978   }
979
980   /* Initialize random number generator */
981   client->rng = silc_rng_alloc();
982   if (!client->rng)
983     return FALSE;
984   silc_rng_init(client->rng);
985   silc_rng_global_init(client->rng);
986
987   /* Initialize the scheduler */
988   client->schedule = silc_schedule_init(0, client);
989   if (!client->schedule)
990     return FALSE;
991
992   /* Allocate client lock */
993   silc_mutex_alloc(&client->internal->lock);
994
995   /* Register commands */
996   silc_client_commands_register(client);
997
998   /* Start packet engine */
999   client->internal->packet_engine =
1000     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1001                              client);
1002   if (!client->internal->packet_engine)
1003     return FALSE;
1004
1005   /* Initialize and start the client FSM */
1006   client->internal->running = running;
1007   client->internal->running_context = context;
1008   silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1009   silc_fsm_event_init(&client->internal->wait_event, &client->internal->fsm);
1010   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1011
1012   /* Signal the application when we are running */
1013   client->internal->run_callback = TRUE;
1014   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1015
1016   return TRUE;
1017 }
1018
1019 /* Starts the SILC client FSM machine and blocks here.  When this returns
1020    the client has ended. */
1021
1022 void silc_client_run(SilcClient client)
1023 {
1024   SILC_LOG_DEBUG(("Starting SILC client"));
1025
1026   /* Run the scheduler */
1027   silc_schedule(client->schedule);
1028 }
1029
1030 /* Call scheduler one iteration and return. */
1031
1032 void silc_client_run_one(SilcClient client)
1033 {
1034   if (silc_fsm_is_started(&client->internal->fsm))
1035     silc_schedule_one(client->schedule, 0);
1036 }
1037
1038 /* Stops the client. This is called to stop the client and thus to stop
1039    the program. */
1040
1041 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1042                       void *context)
1043 {
1044   SILC_LOG_DEBUG(("Stopping client"));
1045
1046   client->internal->running = (SilcClientRunning)stopped;
1047   client->internal->running_context = context;
1048
1049   /* Signal to stop */
1050   client->internal->stop = TRUE;
1051   SILC_FSM_EVENT_SIGNAL(&client->internal->wait_event);
1052 }