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