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