The silc_client_connect_to_[server|client] and
[silc.git] / lib / silcclient / client.c
1 /*
2
3   client.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2006 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_SEMA_POST(&client->internal->wait_event);
63
64   silc_fsm_free(fsm);
65 }
66
67
68 /* Packet FSM thread destructor */
69
70 static void silc_client_packet_destructor(SilcFSMThread thread,
71                                           void *thread_context,
72                                           void *destructor_context)
73 {
74   SilcClientConnection conn = thread_context;
75
76   /* Add thread back to thread pool */
77   silc_list_add(conn->internal->thread_pool, thread);
78   if (silc_list_count(conn->internal->thread_pool) == 1)
79     silc_list_start(conn->internal->thread_pool);
80 }
81
82 /* Packet engine callback to receive a packet */
83
84 static SilcBool silc_client_packet_receive(SilcPacketEngine engine,
85                                            SilcPacketStream stream,
86                                            SilcPacket packet,
87                                            void *callback_context,
88                                            void *stream_context)
89 {
90   SilcClientConnection conn = stream_context;
91   SilcFSMThread thread;
92
93   /* Packets we do not handle */
94   switch (packet->type) {
95   case SILC_PACKET_HEARTBEAT:
96   case SILC_PACKET_SUCCESS:
97   case SILC_PACKET_FAILURE:
98   case SILC_PACKET_REJECT:
99   case SILC_PACKET_KEY_EXCHANGE:
100   case SILC_PACKET_KEY_EXCHANGE_1:
101   case SILC_PACKET_KEY_EXCHANGE_2:
102   case SILC_PACKET_REKEY_DONE:
103   case SILC_PACKET_CONNECTION_AUTH:
104   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
105     return FALSE;
106     break;
107   }
108
109   /* Get packet processing thread */
110   thread = silc_list_get(conn->internal->thread_pool);
111   if (!thread) {
112     thread = silc_fsm_thread_alloc(&conn->internal->fsm, conn,
113                                    silc_client_packet_destructor, NULL, FALSE);
114     if (!thread)
115       return FALSE;
116   } else {
117     silc_list_del(conn->internal->thread_pool, thread);
118     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
119                          silc_client_packet_destructor, NULL, FALSE);
120   }
121
122   /* Process packet in thread */
123   silc_fsm_set_state_context(thread, packet);
124   silc_fsm_start_sync(thread, silc_client_connection_st_packet);
125
126   return TRUE;
127 }
128
129 /* Packet engine callback to indicate end of stream */
130
131 static void silc_client_packet_eos(SilcPacketEngine engine,
132                                    SilcPacketStream stream,
133                                    void *callback_context,
134                                    void *stream_context)
135 {
136   SilcClientConnection conn = stream_context;
137   SilcClient client = conn->client;
138
139   SILC_LOG_DEBUG(("Remote disconnected connection"));
140
141   /* Call connection callback */
142   if (!conn->internal->callback_called)
143     conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, 0, NULL,
144                    conn->callback_context);
145   conn->internal->callback_called = TRUE;
146
147   /* Signal to close connection */
148   if (!conn->internal->disconnected) {
149     conn->internal->disconnected = TRUE;
150     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
151   }
152 }
153
154 /* Packet engine callback to indicate error */
155
156 static void silc_client_packet_error(SilcPacketEngine engine,
157                                      SilcPacketStream stream,
158                                      SilcPacketError error,
159                                      void *callback_context,
160                                      void *stream_context)
161 {
162   /* Nothing */
163 }
164
165 /* Packet stream callbacks */
166 static SilcPacketCallbacks silc_client_stream_cbs =
167 {
168   silc_client_packet_receive,
169   silc_client_packet_eos,
170   silc_client_packet_error
171 };
172
173 /* FSM destructor */
174
175 void silc_client_fsm_destructor(SilcFSM fsm, void *fsm_context,
176                                 void *destructor_context)
177 {
178   silc_fsm_free(fsm);
179 }
180
181 /* Connect abort operation */
182
183 static void silc_client_connect_abort(SilcAsyncOperation op, void *context)
184 {
185   SilcClientConnection conn = context;
186
187   SILC_LOG_DEBUG(("Connection %p aborted by application", conn));
188   conn->internal->aborted = TRUE;
189
190   /* Signal to close connection */
191   if (!conn->internal->disconnected) {
192     conn->internal->disconnected = TRUE;
193     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
194   }
195 }
196
197 /************************** Connection's machine ****************************/
198
199 /* Start the connection's state machine.  If threads are in use the machine
200    is always executed in a real thread. */
201
202 SILC_FSM_STATE(silc_client_connection_st_start)
203 {
204   SilcClientConnection conn = fsm_context;
205   SilcFSM connfsm;
206
207   /* Take scheduler for connection */
208   conn->internal->schedule = silc_fsm_get_schedule(fsm);
209
210   /*** Run connection machine */
211   connfsm = &conn->internal->fsm;
212   silc_fsm_init(connfsm, conn, silc_client_connection_destructor,
213                 fsm, conn->internal->schedule);
214   silc_fsm_sema_init(&conn->internal->wait_event, connfsm, 0);
215   silc_fsm_start_sync(connfsm, silc_client_connection_st_run);
216
217   /* Schedule any events set in initialization */
218   if (conn->internal->connect)
219     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
220   if (conn->internal->key_exchange)
221     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
222
223   /* Wait until this thread is terminated from the machine destructor */
224   return SILC_FSM_WAIT;
225 }
226
227 /* Connection machine main state.  This handles various connection related
228    events, but not packet processing.  It's done in dedicated packet
229    processing FSM thread. */
230
231 SILC_FSM_STATE(silc_client_connection_st_run)
232 {
233   SilcClientConnection conn = fsm_context;
234   SilcFSMThread thread;
235
236   /* Wait for events */
237   SILC_FSM_SEMA_WAIT(&conn->internal->wait_event);
238
239   /* Process events */
240   thread = &conn->internal->event_thread;
241
242   if (conn->internal->connect) {
243     SILC_LOG_DEBUG(("Event: connect"));
244     conn->internal->connect = FALSE;
245
246     /*** Event: connect */
247     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
248                          NULL, NULL, FALSE);
249     silc_fsm_start_sync(thread, silc_client_st_connect);
250     return SILC_FSM_CONTINUE;
251   }
252
253   if (conn->internal->key_exchange) {
254     SILC_LOG_DEBUG(("Event: key exchange"));
255     conn->internal->key_exchange = FALSE;
256
257     /*** Event: key exchange */
258     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
259                          NULL, NULL, FALSE);
260     silc_fsm_start_sync(thread, silc_client_st_connect_set_stream);
261     return SILC_FSM_CONTINUE;
262   }
263
264   if (conn->internal->rekeying) {
265     SILC_LOG_DEBUG(("Event: rekey"));
266     conn->internal->rekeying = FALSE;
267
268     /*** Event: rekey */
269     silc_fsm_thread_init(thread, &conn->internal->fsm, conn,
270                          NULL, NULL, FALSE);
271     silc_fsm_start_sync(thread, silc_client_st_rekey);
272     return SILC_FSM_CONTINUE;
273   }
274
275   if (conn->internal->disconnected) {
276     /** Event: disconnected */
277     SILC_LOG_DEBUG(("Event: disconnected"));
278     silc_fsm_next(fsm, silc_client_connection_st_close);
279     return SILC_FSM_YIELD;
280   }
281
282   /* NOT REACHED */
283   SILC_ASSERT(FALSE);
284   return SILC_FSM_CONTINUE;
285 }
286
287 /* Packet processor thread.  Each incoming packet is processed in FSM
288    thread in this state.  The thread is run in the connection machine. */
289
290 SILC_FSM_STATE(silc_client_connection_st_packet)
291 {
292   SilcClientConnection conn = fsm_context;
293   SilcPacket packet = state_context;
294
295   SILC_LOG_DEBUG(("Parsing %s packet", silc_get_packet_name(packet->type)));
296
297   switch (packet->type) {
298
299   case SILC_PACKET_PRIVATE_MESSAGE:
300     /** Private message */
301     silc_fsm_next(fsm, silc_client_private_message);
302     break;
303
304   case SILC_PACKET_CHANNEL_MESSAGE:
305     /** Channel message */
306     silc_fsm_next(fsm, silc_client_channel_message);
307     break;
308
309   case SILC_PACKET_FTP:
310     /* File transfer packet */
311     //    silc_client_ftp(client, conn, packet);
312     break;
313
314   case SILC_PACKET_CHANNEL_KEY:
315     /** Channel key */
316     silc_fsm_next(fsm, silc_client_channel_key);
317     break;
318
319   case SILC_PACKET_COMMAND_REPLY:
320     /** Command reply */
321     silc_fsm_next(fsm, silc_client_command_reply);
322     break;
323
324   case SILC_PACKET_NOTIFY:
325     /** Notify */
326     silc_fsm_next(fsm, silc_client_notify);
327     break;
328
329   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
330     /* Private message key indicator */
331     silc_fsm_next(fsm, silc_client_private_message_key);
332     break;
333
334   case SILC_PACKET_DISCONNECT:
335     /** Disconnect */
336     silc_fsm_next(fsm, silc_client_disconnect);
337     break;
338
339   case SILC_PACKET_ERROR:
340     /* Error by server */
341     silc_fsm_next(fsm, silc_client_error);
342     break;
343
344   case SILC_PACKET_KEY_AGREEMENT:
345     /** Key agreement */
346     silc_fsm_next(fsm, silc_client_key_agreement);
347     break;
348
349   case SILC_PACKET_COMMAND:
350     /** Command packet */
351     silc_fsm_next(fsm, silc_client_command);
352     break;
353
354   case SILC_PACKET_NEW_ID:
355     /** New ID */
356     silc_fsm_next(fsm, silc_client_new_id);
357     break;
358
359   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
360     /* Reply to connection authentication request to resolve authentication
361        method from server. */
362     //    silc_client_connection_auth_request(client, conn, packet);
363     break;
364
365   case SILC_PACKET_REKEY:
366     /* Signal to start rekey */
367     conn->internal->rekey_responder = TRUE;
368     conn->internal->rekeying = TRUE;
369     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
370
371     silc_packet_free(packet);
372     return SILC_FSM_FINISH;
373     break;
374
375   default:
376     silc_packet_free(packet);
377     return SILC_FSM_FINISH;
378     break;
379   }
380
381   return SILC_FSM_CONTINUE;
382 }
383
384 /* Disconnection event to close remote connection.  We close the connection
385    and finish the connection machine in this state.  The connection context
386    is deleted in the machine destructor.  The connection callback must be
387    already called back to application before getting here. */
388
389 SILC_FSM_STATE(silc_client_connection_st_close)
390 {
391   SilcClientConnection conn = fsm_context;
392   SilcClientCommandContext cmd;
393
394   /* Finish running command threads.  This will also finish waiting packet
395      thread, as they are always waiting for some command.  If any thread is
396      waiting something else than command, they must be finished explicitly. */
397   if (silc_list_count(conn->internal->pending_commands)) {
398     SILC_LOG_DEBUG(("Finish pending commands"));
399     silc_list_start(conn->internal->pending_commands);
400     while ((cmd = silc_list_get(conn->internal->pending_commands))) {
401       if (silc_fsm_is_started(&cmd->thread)) {
402         cmd->verbose = FALSE;
403         silc_fsm_continue_sync(&cmd->thread);
404       }
405     }
406
407     /* Give threads time to finish */
408     return SILC_FSM_YIELD;
409   }
410
411   /* Abort ongoing events */
412   if (conn->internal->op) {
413     SILC_LOG_DEBUG(("Abort event"));
414     silc_async_abort(conn->internal->op, NULL, NULL);
415     conn->internal->op = NULL;
416   }
417
418   /* If event thread is running, finish it. */
419   if (silc_fsm_is_started(&conn->internal->event_thread)) {
420     SILC_LOG_DEBUG(("Finish event thread"));
421     silc_fsm_continue_sync(&conn->internal->event_thread);
422     return SILC_FSM_YIELD;
423   }
424
425   SILC_LOG_DEBUG(("Closing remote connection"));
426
427   /* Close connection */
428   silc_packet_stream_destroy(conn->stream);
429
430   SILC_LOG_DEBUG(("Finishing connection machine"));
431
432   return SILC_FSM_FINISH;
433 }
434
435 /* Received error packet from server.  Send it to application. */
436
437 SILC_FSM_STATE(silc_client_error)
438 {
439   SilcClientConnection conn = fsm_context;
440   SilcClient client = conn->client;
441   SilcPacket packet = state_context;
442   char *msg;
443
444   msg = silc_memdup(silc_buffer_data(&packet->buffer),
445                     silc_buffer_len(&packet->buffer));
446   if (msg)
447     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, msg);
448
449   silc_free(msg);
450   silc_packet_free(packet);
451
452   return SILC_FSM_FINISH;
453 }
454
455 /* Received disconnect packet from server.  We close the connection and
456    send the disconnect message to application. */
457
458 SILC_FSM_STATE(silc_client_disconnect)
459 {
460   SilcClientConnection conn = fsm_context;
461   SilcClient client = conn->client;
462   SilcPacket packet = state_context;
463   SilcStatus status;
464   char *message = NULL;
465
466   SILC_LOG_DEBUG(("Server disconnected"));
467
468   if (silc_buffer_len(&packet->buffer) < 1) {
469     silc_packet_free(packet);
470     return SILC_FSM_FINISH;
471   }
472
473   status = (SilcStatus)packet->buffer.data[0];
474
475   silc_buffer_pull(&packet->buffer, 1);
476   if (silc_buffer_len(&packet->buffer) > 1 &&
477       silc_utf8_valid(silc_buffer_data(&packet->buffer),
478                       silc_buffer_len(&packet->buffer)))
479     message = silc_memdup(silc_buffer_data(&packet->buffer),
480                           silc_buffer_len(&packet->buffer));
481
482   /* Call connection callback */
483   if (!conn->internal->callback_called)
484     conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED, status,
485                    message, conn->callback_context);
486   conn->internal->callback_called = TRUE;
487
488   silc_free(message);
489   silc_packet_free(packet);
490
491   /* Signal to close connection */
492   if (!conn->internal->disconnected) {
493     conn->internal->disconnected = TRUE;
494     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
495   }
496
497   return SILC_FSM_FINISH;
498 }
499
500 /*************************** Main client machine ****************************/
501
502 /* The client's main state where we wait for various events */
503
504 SILC_FSM_STATE(silc_client_st_run)
505 {
506   SilcClient client = fsm_context;
507
508   /* Wait for events */
509   SILC_FSM_SEMA_WAIT(&client->internal->wait_event);
510
511   /* Process events */
512
513   if (client->internal->run_callback && client->internal->running) {
514     /* Call running callbcak back to application */
515     SILC_LOG_DEBUG(("We are up, call running callback"));
516     client->internal->run_callback = FALSE;
517     client->internal->running(client, client->internal->running_context);
518     return SILC_FSM_CONTINUE;
519   }
520
521   if (client->internal->connection_closed) {
522     /* A connection finished */
523     SILC_LOG_DEBUG(("Event: connection closed"));
524     client->internal->connection_closed = FALSE;
525     if (silc_atomic_get_int16(&client->internal->conns) == 0 &&
526         client->internal->stop)
527       SILC_FSM_SEMA_POST(&client->internal->wait_event);
528     return SILC_FSM_CONTINUE;
529   }
530
531   if (client->internal->stop) {
532     /* Stop client libarry.  If we have running connections, wait until
533        they finish first. */
534     SILC_LOG_DEBUG(("Event: stop"));
535     if (silc_atomic_get_int16(&client->internal->conns) == 0)
536       silc_fsm_next(fsm, silc_client_st_stop);
537     return SILC_FSM_CONTINUE;
538   }
539
540   /* NOT REACHED */
541   SILC_ASSERT(FALSE);
542   return SILC_FSM_CONTINUE;
543 }
544
545 /* Stop event.  Stops the client library. */
546
547 SILC_FSM_STATE(silc_client_st_stop)
548 {
549   SilcClient client = fsm_context;
550
551   SILC_LOG_DEBUG(("Client stopped"));
552
553   /* Stop scheduler */
554   silc_schedule_stop(client->schedule);
555   silc_client_commands_unregister(client);
556
557   /* Call stopped callback to application */
558   if (client->internal->running)
559     client->internal->running(client, client->internal->running_context);
560
561   return SILC_FSM_FINISH;
562 }
563
564 /******************************* Private API ********************************/
565
566 /* Adds new connection.  Creates the connection context and returns it. */
567
568 static SilcClientConnection
569 silc_client_add_connection(SilcClient client,
570                            SilcConnectionType conn_type,
571                            SilcClientConnectionParams *params,
572                            SilcPublicKey public_key,
573                            SilcPrivateKey private_key,
574                            char *remote_host, int port,
575                            SilcClientConnectCallback callback,
576                            void *context)
577 {
578   SilcClientConnection conn;
579   SilcFSMThread thread;
580
581   if (!callback)
582     return NULL;
583
584   SILC_LOG_DEBUG(("Adding new connection to %s:%d", remote_host, port));
585
586   conn = silc_calloc(1, sizeof(*conn));
587   if (!conn)
588     return NULL;
589
590   conn->client = client;
591   conn->public_key = public_key;
592   conn->private_key = private_key;
593   conn->remote_host = strdup(remote_host);
594   conn->remote_port = port ? port : 706;
595   conn->type = conn_type;
596   conn->callback = callback;
597   conn->callback_context = context;
598
599   conn->internal = silc_calloc(1, sizeof(*conn->internal));
600   if (!conn->internal) {
601     silc_free(conn);
602     return NULL;
603   }
604   conn->internal->retry_timer = SILC_CLIENT_RETRY_MIN;
605   silc_mutex_alloc(&conn->internal->lock);
606   silc_atomic_init16(&conn->internal->cmd_ident, 0);
607
608   if (!silc_hash_alloc("sha1", &conn->internal->sha1hash)) {
609     silc_free(conn);
610     silc_free(conn->internal);
611     return NULL;
612   }
613
614   /* Set parameters */
615   if (params)
616     conn->internal->params = *params;
617   if (!conn->internal->params.rekey_secs)
618     conn->internal->params.rekey_secs = 3600;
619 #ifndef SILC_DIST_INPLACE
620   if (conn->internal->params.rekey_secs < 300)
621     conn->internal->params.rekey_secs = 300;
622 #endif /* SILC_DIST_INPLACE */
623
624   conn->internal->verbose = TRUE;
625   silc_list_init(conn->internal->pending_commands,
626                  struct SilcClientCommandContextStruct, next);
627   silc_list_init(conn->internal->thread_pool, SilcFSMThreadStruct, next);
628
629   conn->internal->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
630                                                     NULL, NULL);
631   conn->internal->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL,
632                                                      NULL, NULL);
633   conn->internal->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER,
634                                                     NULL, NULL);
635   if (!conn->internal->client_cache || !conn->internal->channel_cache ||
636       !conn->internal->server_cache) {
637     silc_client_del_connection(client, conn);
638     return NULL;
639   }
640
641   conn->internal->ftp_sessions = silc_dlist_init();
642
643   /* Initiatlize our async operation so that application may abort us
644      while were connecting. */
645   conn->internal->cop = silc_async_alloc(silc_client_connect_abort,
646                                          NULL, conn);
647   if (!conn->internal->cop) {
648     silc_client_del_connection(client, conn);
649     return NULL;
650   }
651
652   /* Run the connection state machine.  If threads are in use the machine
653      is always run in a real thread. */
654   thread = silc_fsm_thread_alloc(&client->internal->fsm, conn,
655                                  silc_client_connection_finished, NULL,
656                                  client->internal->params->threads);
657   if (!thread) {
658     silc_client_del_connection(client, conn);
659     return NULL;
660   }
661   silc_fsm_set_state_context(thread, client);
662   silc_fsm_start(thread, silc_client_connection_st_start);
663
664   SILC_LOG_DEBUG(("New connection %p", conn));
665   silc_atomic_add_int16(&client->internal->conns, 1);
666
667   return conn;
668 }
669
670 /* Deletes connection.  This is always called from the connection machine
671    destructor.  Do not call this directly other places. */
672
673 void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
674 {
675   SilcList list;
676   SilcIDCacheEntry entry;
677   SilcFSMThread thread;
678
679   SILC_LOG_DEBUG(("Freeing connection %p", conn));
680
681   silc_schedule_task_del_by_context(conn->internal->schedule, conn);
682
683   /* Free all cache entries */
684   if (silc_idcache_get_all(conn->internal->server_cache, &list)) {
685     silc_list_start(list);
686     while ((entry = silc_list_get(list)))
687       silc_client_del_server(client, conn, entry->context);
688   }
689   if (silc_idcache_get_all(conn->internal->channel_cache, &list)) {
690     silc_list_start(list);
691     while ((entry = silc_list_get(list))) {
692       silc_client_empty_channel(client, conn, entry->context);
693       silc_client_del_channel(client, conn, entry->context);
694     }
695   }
696   if (silc_idcache_get_all(conn->internal->client_cache, &list)) {
697     silc_list_start(list);
698     while ((entry = silc_list_get(list)))
699       silc_client_del_client(client, conn, entry->context);
700   }
701
702   /* Free ID caches */
703   if (conn->internal->client_cache)
704     silc_idcache_free(conn->internal->client_cache);
705   if (conn->internal->channel_cache)
706     silc_idcache_free(conn->internal->channel_cache);
707   if (conn->internal->server_cache)
708     silc_idcache_free(conn->internal->server_cache);
709
710   /* Free thread pool */
711   silc_list_start(conn->internal->thread_pool);
712   while ((thread = silc_list_get(conn->internal->thread_pool)))
713     silc_fsm_free(thread);
714
715   silc_free(conn->remote_host);
716   silc_buffer_free(conn->internal->local_idp);
717   silc_buffer_free(conn->internal->remote_idp);
718   silc_mutex_free(conn->internal->lock);
719   if (conn->internal->hash)
720     silc_hash_free(conn->internal->hash);
721   if (conn->internal->sha1hash)
722     silc_hash_free(conn->internal->sha1hash);
723   silc_atomic_uninit16(&conn->internal->cmd_ident);
724
725   silc_free(conn->internal);
726   memset(conn, 'F', sizeof(*conn));
727   silc_free(conn);
728 }
729
730
731 /******************************* Client API *********************************/
732
733 /* Connects to remote server.  This is the main routine used to connect
734    to remote SILC server.  Performs key exchange also.  Returns the
735    connection context to the connection callback. */
736
737 SilcAsyncOperation
738 silc_client_connect_to_server(SilcClient client,
739                               SilcClientConnectionParams *params,
740                               SilcPublicKey public_key,
741                               SilcPrivateKey private_key,
742                               char *remote_host, int port,
743                               SilcClientConnectCallback callback,
744                               void *context)
745 {
746   SilcClientConnection conn;
747
748   if (!client || !remote_host)
749     return NULL;
750
751   /* Add new connection */
752   conn = silc_client_add_connection(client, SILC_CONN_SERVER, params,
753                                     public_key, private_key, remote_host,
754                                     port, callback, context);
755   if (!conn) {
756     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
757     return NULL;
758   }
759
760   client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
761                              "Connecting to port %d of server %s",
762                              port, remote_host);
763
764   /* Signal connection machine to start connecting */
765   conn->internal->connect = TRUE;
766   return conn->internal->cop;
767 }
768
769 /* Connects to remote client.  Performs key exchange also.  Returns the
770    connection context to the connection callback. */
771
772 SilcAsyncOperation
773 silc_client_connect_to_client(SilcClient client,
774                               SilcClientConnectionParams *params,
775                               SilcPublicKey public_key,
776                               SilcPrivateKey private_key,
777                               char *remote_host, int port,
778                               SilcClientConnectCallback callback,
779                               void *context)
780 {
781   SilcClientConnection conn;
782
783   if (!client || !remote_host)
784     return NULL;
785
786   /* Add new connection */
787   conn = silc_client_add_connection(client, SILC_CONN_CLIENT, params,
788                                     public_key, private_key, remote_host,
789                                     port, callback, context);
790   if (!conn) {
791     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
792     return NULL;
793   }
794
795   /* Signal connection machine to start connecting */
796   conn->internal->connect = TRUE;
797   return conn->internal->cop;
798 }
799
800 /* Starts key exchange in the remote stream indicated by `stream'.  This
801    creates the connection context and returns it in the connection callback. */
802
803 SilcAsyncOperation
804 silc_client_key_exchange(SilcClient client,
805                          SilcClientConnectionParams *params,
806                          SilcPublicKey public_key,
807                          SilcPrivateKey private_key,
808                          SilcStream stream,
809                          SilcConnectionType conn_type,
810                          SilcClientConnectCallback callback,
811                          void *context)
812 {
813   SilcClientConnection conn;
814   const char *host;
815   SilcUInt16 port;
816
817   if (!client || !stream)
818     return NULL;
819
820   if (!silc_socket_stream_get_info(stream, NULL, &host, NULL, &port)) {
821     SILC_LOG_ERROR(("Socket stream does not have remote host name set"));
822     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
823     return NULL;
824   }
825
826   /* Add new connection */
827   conn = silc_client_add_connection(client, conn_type, params,
828                                     public_key, private_key,
829                                     (char *)host, port, callback, context);
830   if (!conn) {
831     callback(client, NULL, SILC_CLIENT_CONN_ERROR, 0, NULL, context);
832     return NULL;
833   }
834   conn->stream = (void *)stream;
835
836   /* Signal connection to start key exchange */
837   conn->internal->key_exchange = TRUE;
838   return conn->internal->cop;
839 }
840
841 /* Closes remote connection */
842
843 void silc_client_close_connection(SilcClient client,
844                                   SilcClientConnection conn)
845 {
846   SILC_LOG_DEBUG(("Closing connection %p", conn));
847
848   /* Signal to close connection */
849   if (!conn->internal->disconnected) {
850     conn->internal->disconnected = TRUE;
851     SILC_FSM_SEMA_POST(&conn->internal->wait_event);
852   }
853 }
854
855 #if 0
856 /* Finalizes the connection to the remote SILC server. This is called
857    after authentication protocol has been completed. This send our
858    user information to the server to receive our client ID from
859    server. */
860
861 SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
862 {
863   SilcProtocol protocol = (SilcProtocol)context;
864   SilcClientConnAuthInternalContext *ctx =
865     (SilcClientConnAuthInternalContext *)protocol->context;
866   SilcClient client = (SilcClient)ctx->client;
867   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
868   SilcBuffer packet;
869
870   SILC_LOG_DEBUG(("Start"));
871
872   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
873       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
874     /* Error occured during protocol */
875     SILC_LOG_DEBUG(("Error during authentication protocol"));
876     ctx->status = SILC_CLIENT_CONN_ERROR_AUTH;
877     goto err;
878   }
879
880   if (conn->internal->params.detach_data) {
881     /* Send RESUME_CLIENT packet to the server, which is used to resume
882        old detached session back. */
883     SilcBuffer auth;
884     SilcClientID *old_client_id;
885     unsigned char *old_id;
886     SilcUInt16 old_id_len;
887
888     if (!silc_client_process_detach_data(client, conn, &old_id, &old_id_len)) {
889       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
890       goto err;
891     }
892
893     old_client_id = silc_id_str2id(old_id, old_id_len, SILC_ID_CLIENT);
894     if (!old_client_id) {
895       silc_free(old_id);
896       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
897       goto err;
898     }
899
900     /* Generate authentication data that server will verify */
901     auth = silc_auth_public_key_auth_generate(client->public_key,
902                                               client->private_key,
903                                               client->rng,
904                                               conn->internal->hash,
905                                               old_client_id, SILC_ID_CLIENT);
906     if (!auth) {
907       silc_free(old_client_id);
908       silc_free(old_id);
909       ctx->status = SILC_CLIENT_CONN_ERROR_RESUME;
910       goto err;
911     }
912
913     packet = silc_buffer_alloc_size(2 + old_id_len + auth->len);
914     silc_buffer_format(packet,
915                        SILC_STR_UI_SHORT(old_id_len),
916                        SILC_STR_UI_XNSTRING(old_id, old_id_len),
917                        SILC_STR_UI_XNSTRING(auth->data, auth->len),
918                        SILC_STR_END);
919
920     /* Send the packet */
921     silc_client_packet_send(client, ctx->sock, SILC_PACKET_RESUME_CLIENT,
922                             NULL, 0, NULL, NULL,
923                             packet->data, packet->len, TRUE);
924     silc_buffer_free(packet);
925     silc_buffer_free(auth);
926     silc_free(old_client_id);
927     silc_free(old_id);
928   } else {
929     /* Send NEW_CLIENT packet to the server. We will become registered
930        to the SILC network after sending this packet and we will receive
931        client ID from the server. */
932     packet = silc_buffer_alloc(2 + 2 + strlen(client->username) +
933                                strlen(client->realname));
934     silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
935     silc_buffer_format(packet,
936                        SILC_STR_UI_SHORT(strlen(client->username)),
937                        SILC_STR_UI_XNSTRING(client->username,
938                                             strlen(client->username)),
939                        SILC_STR_UI_SHORT(strlen(client->realname)),
940                        SILC_STR_UI_XNSTRING(client->realname,
941                                             strlen(client->realname)),
942                        SILC_STR_END);
943
944     /* Send the packet */
945     silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
946                             NULL, 0, NULL, NULL,
947                             packet->data, packet->len, TRUE);
948     silc_buffer_free(packet);
949   }
950
951   /* Save remote ID. */
952   conn->remote_id = ctx->dest_id;
953   conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
954   conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
955
956   /* Register re-key timeout */
957   conn->internal->rekey->timeout = client->internal->params->rekey_secs;
958   conn->internal->rekey->context = (void *)client;
959   silc_schedule_task_add(client->schedule, conn->sock->sock,
960                          silc_client_rekey_callback,
961                          (void *)conn->sock, conn->internal->rekey->timeout, 0,
962                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
963
964   silc_protocol_free(protocol);
965   silc_free(ctx->auth_data);
966   silc_socket_free(ctx->sock);
967   silc_free(ctx);
968   conn->sock->protocol = NULL;
969   return;
970
971  err:
972   silc_protocol_free(protocol);
973   silc_free(ctx->auth_data);
974   silc_free(ctx->dest_id);
975   conn->sock->protocol = NULL;
976   silc_socket_free(ctx->sock);
977
978   /* Notify application of failure */
979   silc_schedule_task_add(client->schedule, ctx->sock->sock,
980                          silc_client_connect_failure_auth, ctx,
981                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
982 }
983
984 /* Client session resuming callback.  If the session was resumed
985    this callback is called after the resuming is completed.  This
986    will call the `connect' client operation to the application
987    since it has not been called yet. */
988
989 static void silc_client_resume_session_cb(SilcClient client,
990                                           SilcClientConnection conn,
991                                           SilcBool success,
992                                           void *context)
993 {
994   SilcBuffer sidp;
995
996   /* Notify application that connection is created to server */
997   client->internal->ops->connected(client, conn, success ?
998                                    SILC_CLIENT_CONN_SUCCESS_RESUME :
999                                    SILC_CLIENT_CONN_ERROR_RESUME);
1000
1001   if (success) {
1002     /* Issue INFO command to fetch the real server name and server
1003        information and other stuff. */
1004     silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
1005                                  silc_client_command_reply_info_i, 0,
1006                                  ++conn->cmd_ident);
1007     sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
1008     silc_client_command_send(client, conn, SILC_COMMAND_INFO,
1009                              conn->cmd_ident, 1, 2, sidp->data, sidp->len);
1010     silc_buffer_free(sidp);
1011   }
1012 }
1013
1014 /* Processes incoming connection authentication method request packet.
1015    It is a reply to our previously sent request. The packet can be used
1016    to resolve the authentication method for the current session if the
1017    client does not know it beforehand. */
1018
1019 void silc_client_connection_auth_request(SilcClient client,
1020                                          SilcClientConnection conn,
1021                                          SilcPacketContext *packet)
1022 {
1023   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1024   SilcUInt16 conn_type, auth_meth;
1025   int ret;
1026
1027   /* If we haven't send our request then ignore this one. */
1028   if (!conn->internal->connauth)
1029     return;
1030
1031   /* Parse the payload */
1032   ret = silc_buffer_unformat(packet->buffer,
1033                              SILC_STR_UI_SHORT(&conn_type),
1034                              SILC_STR_UI_SHORT(&auth_meth),
1035                              SILC_STR_END);
1036   if (ret == -1)
1037     auth_meth = SILC_AUTH_NONE;
1038
1039   /* Call the request callback to notify application for received
1040      authentication method information. */
1041   if (conn->internal->connauth->callback)
1042     (*conn->internal->connauth->callback)(client, conn, auth_meth,
1043                                           conn->internal->connauth->context);
1044
1045   silc_schedule_task_del(client->schedule, conn->internal->connauth->timeout);
1046
1047   silc_free(conn->internal->connauth);
1048   conn->internal->connauth = NULL;
1049 }
1050
1051 /* Timeout task callback called if the server does not reply to our
1052    connection authentication method request in the specified time interval. */
1053
1054 SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
1055 {
1056   SilcClientConnection conn = (SilcClientConnection)context;
1057   SilcClient client = conn->client;
1058
1059   if (!conn->internal->connauth)
1060     return;
1061
1062   /* Call the request callback to notify application */
1063   if (conn->internal->connauth->callback)
1064     (*conn->internal->connauth->callback)(client, conn, SILC_AUTH_NONE,
1065                                           conn->internal->connauth->context);
1066
1067   silc_free(conn->internal->connauth);
1068   conn->internal->connauth = NULL;
1069 }
1070
1071 /* This function can be used to request the current authentication method
1072    from the server. This may be called when connecting to the server
1073    and the client library requests the authentication data from the
1074    application. If the application does not know the current authentication
1075    method it can request it from the server using this function.
1076    The `callback' with `context' will be called after the server has
1077    replied back with the current authentication method. */
1078
1079 void
1080 silc_client_request_authentication_method(SilcClient client,
1081                                           SilcClientConnection conn,
1082                                           SilcConnectionAuthRequest callback,
1083                                           void *context)
1084 {
1085   SilcClientConnAuthRequest connauth;
1086   SilcBuffer packet;
1087
1088   assert(client && conn);
1089   connauth = silc_calloc(1, sizeof(*connauth));
1090   connauth->callback = callback;
1091   connauth->context = context;
1092
1093   if (conn->internal->connauth)
1094     silc_free(conn->internal->connauth);
1095
1096   conn->internal->connauth = connauth;
1097
1098   /* Assemble the request packet and send it to the server */
1099   packet = silc_buffer_alloc(4);
1100   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
1101   silc_buffer_format(packet,
1102                      SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
1103                      SILC_STR_UI_SHORT(SILC_AUTH_NONE),
1104                      SILC_STR_END);
1105   silc_client_packet_send(client, conn->sock,
1106                           SILC_PACKET_CONNECTION_AUTH_REQUEST,
1107                           NULL, 0, NULL, NULL,
1108                           packet->data, packet->len, FALSE);
1109   silc_buffer_free(packet);
1110
1111   /* Register a timeout in case server does not reply anything back. */
1112   connauth->timeout =
1113     silc_schedule_task_add(client->schedule, conn->sock->sock,
1114                            silc_client_request_authentication_method_timeout,
1115                            conn,
1116                            client->internal->params->connauth_request_secs, 0,
1117                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1118 }
1119 #endif /* 0 */
1120
1121
1122 /* Allocates new client object. This has to be done before client may
1123    work. After calling this one must call silc_client_init to initialize
1124    the client. The `application' is application specific user data pointer
1125    and caller must free it. */
1126
1127 SilcClient silc_client_alloc(SilcClientOperations *ops,
1128                              SilcClientParams *params,
1129                              void *application,
1130                              const char *version_string)
1131 {
1132   SilcClient new_client;
1133
1134   new_client = silc_calloc(1, sizeof(*new_client));
1135   if (!new_client)
1136     return NULL;
1137   new_client->application = application;
1138
1139   new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
1140   if (!new_client->internal) {
1141     silc_free(new_client);
1142     return NULL;
1143   }
1144   new_client->internal->ops = ops;
1145   new_client->internal->params =
1146     silc_calloc(1, sizeof(*new_client->internal->params));
1147   if (!version_string)
1148     version_string = silc_version_string;
1149   new_client->internal->silc_client_version = strdup(version_string);
1150
1151   if (params)
1152     memcpy(new_client->internal->params, params, sizeof(*params));
1153
1154   if (!new_client->internal->params->connauth_request_secs)
1155     new_client->internal->params->connauth_request_secs = 2;
1156
1157   new_client->internal->params->
1158     nickname_format[sizeof(new_client->internal->
1159                            params->nickname_format) - 1] = 0;
1160
1161   silc_atomic_init16(&new_client->internal->conns, 0);
1162
1163   return new_client;
1164 }
1165
1166 /* Frees client object and its internals. */
1167
1168 void silc_client_free(SilcClient client)
1169 {
1170   silc_schedule_uninit(client->schedule);
1171
1172   if (client->rng)
1173     silc_rng_free(client->rng);
1174
1175   if (!client->internal->params->dont_register_crypto_library) {
1176     silc_cipher_unregister_all();
1177     silc_pkcs_unregister_all();
1178     silc_hash_unregister_all();
1179     silc_hmac_unregister_all();
1180   }
1181
1182   silc_atomic_uninit16(&client->internal->conns);
1183   silc_free(client->username);
1184   silc_free(client->hostname);
1185   silc_free(client->realname);
1186   silc_free(client->internal->params);
1187   silc_free(client->internal->silc_client_version);
1188   silc_free(client->internal);
1189   silc_free(client);
1190 }
1191
1192 /* Initializes the client. This makes all the necessary steps to make
1193    the client ready to be run. One must call silc_client_run to run the
1194    client. Returns FALSE if error occured, TRUE otherwise. */
1195
1196 SilcBool silc_client_init(SilcClient client, const char *username,
1197                           const char *hostname, const char *realname,
1198                           SilcClientRunning running, void *context)
1199 {
1200   SILC_LOG_DEBUG(("Initializing client"));
1201
1202   if (!client)
1203     return FALSE;
1204
1205   if (!username || !hostname) {
1206     SILC_LOG_ERROR(("Username, hostname and realname must be given to "
1207                     "silc_client_init"));
1208     return FALSE;
1209   }
1210   if (!realname)
1211     realname = username;
1212
1213   /* Validate essential strings */
1214   if (!silc_identifier_verify(username, strlen(username),
1215                               SILC_STRING_UTF8, 128)) {
1216     SILC_LOG_ERROR(("Malformed username '%s'. Username must be UTF-8 string",
1217                     client->username));
1218     return FALSE;
1219   }
1220   if (!silc_identifier_verify(hostname, strlen(hostname),
1221                               SILC_STRING_UTF8, 256)) {
1222     SILC_LOG_ERROR(("Malformed hostname '%s'. Hostname must be UTF-8 string",
1223                     client->hostname));
1224     return FALSE;
1225   }
1226   if (!silc_utf8_valid(realname, strlen(realname))) {
1227     SILC_LOG_ERROR(("Malformed realname '%s'. Realname must be UTF-8 string",
1228                     client->realname));
1229     return FALSE;
1230   }
1231
1232   /* Take the name strings */
1233   client->username = strdup(username);
1234   client->hostname = strdup(hostname);
1235   client->realname = strdup(realname);
1236   if (!username || !hostname || !realname)
1237     return FALSE;
1238
1239   if (!client->internal->params->dont_register_crypto_library) {
1240     /* Initialize the crypto library.  If application has done this already
1241        this has no effect.  Also, we will not be overriding something
1242        application might have registered earlier. */
1243     silc_cipher_register_default();
1244     silc_pkcs_register_default();
1245     silc_hash_register_default();
1246     silc_hmac_register_default();
1247   }
1248
1249   /* Initialize random number generator */
1250   client->rng = silc_rng_alloc();
1251   if (!client->rng)
1252     return FALSE;
1253   silc_rng_init(client->rng);
1254   silc_rng_global_init(client->rng);
1255
1256   /* Initialize the scheduler */
1257   client->schedule = silc_schedule_init(0, client);
1258   if (!client->schedule)
1259     return FALSE;
1260
1261   /* Start packet engine */
1262   client->internal->packet_engine =
1263     silc_packet_engine_start(client->rng, FALSE, &silc_client_stream_cbs,
1264                              client);
1265   if (!client->internal->packet_engine)
1266     return FALSE;
1267
1268   /* Allocate client lock */
1269   silc_mutex_alloc(&client->internal->lock);
1270
1271   /* Register commands */
1272   silc_client_commands_register(client);
1273
1274   /* Initialize and start the client FSM */
1275   client->internal->running = running;
1276   client->internal->running_context = context;
1277   silc_fsm_init(&client->internal->fsm, client, NULL, NULL, client->schedule);
1278   silc_fsm_sema_init(&client->internal->wait_event, &client->internal->fsm, 0);
1279   silc_fsm_start_sync(&client->internal->fsm, silc_client_st_run);
1280
1281   /* Signal the application when we are running */
1282   client->internal->run_callback = TRUE;
1283   SILC_FSM_SEMA_POST(&client->internal->wait_event);
1284
1285   return TRUE;
1286 }
1287
1288 /* Starts the SILC client FSM machine and blocks here.  When this returns
1289    the client has ended. */
1290
1291 void silc_client_run(SilcClient client)
1292 {
1293   SILC_LOG_DEBUG(("Starting SILC client"));
1294
1295   /* Run the scheduler */
1296   silc_schedule(client->schedule);
1297 }
1298
1299 /* Call scheduler one iteration and return.  This cannot be called if threads
1300    are in use. */
1301
1302 void silc_client_run_one(SilcClient client)
1303 {
1304   if (silc_fsm_is_started(&client->internal->fsm))
1305     silc_schedule_one(client->schedule, 0);
1306 }
1307
1308 /* Stops the client. This is called to stop the client and thus to stop
1309    the program. */
1310
1311 void silc_client_stop(SilcClient client, SilcClientStopped stopped,
1312                       void *context)
1313 {
1314   SILC_LOG_DEBUG(("Stopping client"));
1315
1316   client->internal->running = (SilcClientRunning)stopped;
1317   client->internal->running_context = context;
1318
1319   /* Signal to stop */
1320   client->internal->stop = TRUE;
1321   SILC_FSM_SEMA_POST(&client->internal->wait_event);
1322 }