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