5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 Pekka Riikonen
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.
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.
22 #include "silcclient.h"
23 #include "client_internal.h"
26 silc_client_connect_to_client(SilcClient client,
27 SilcClientConnection conn, int port,
28 char *host, void *context);
30 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
31 SILC_TASK_CALLBACK(silc_client_ftp_connected);
32 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
35 /* File transmission session */
36 struct SilcClientFtpSessionStruct {
37 SilcUInt32 session_id;
39 SilcClientConnection conn;
40 SilcClientEntry client_entry;
42 SilcSocketConnection sock;
49 SilcClientFileMonitor monitor;
50 void *monitor_context;
51 SilcClientFileAskName ask_name;
52 void *ask_name_context;
57 SilcSFTPFilesystem fs;
58 unsigned int server : 1; /* File sender sets this to TRUE */
59 unsigned int bound : 1;
61 SilcSFTPHandle dir_handle;
62 SilcSFTPHandle read_handle;
64 SilcUInt64 read_offset;
68 SILC_TASK_CALLBACK(silc_client_ftp_connected)
70 SilcClientInternalConnectContext *ctx =
71 (SilcClientInternalConnectContext *)context;
72 SilcClient client = ctx->client;
73 SilcClientConnection conn = ctx->conn;
74 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
75 int opt, opt_len = sizeof(opt);
77 SILC_LOG_DEBUG(("Start"));
79 /* Check the socket status as it might be in error */
80 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
83 /* Connection failed but lets try again */
84 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
85 "Could not connect to client %s: %s",
86 ctx->host, strerror(opt));
87 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
88 "Connecting to port %d of client %s resumed",
89 ctx->port, ctx->host);
91 /* Unregister old connection try */
92 silc_schedule_unset_listen_fd(client->schedule, fd);
93 silc_net_close_connection(fd);
94 silc_schedule_task_del(client->schedule, ctx->task);
97 silc_client_connect_to_client_internal(ctx);
100 /* Connection failed and we won't try anymore */
101 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
102 "Could not connect to client %s: %s",
103 ctx->host, strerror(opt));
104 silc_schedule_unset_listen_fd(client->schedule, fd);
105 silc_net_close_connection(fd);
106 silc_schedule_task_del(client->schedule, ctx->task);
108 silc_client_ftp_session_free(session);
113 silc_schedule_unset_listen_fd(client->schedule, fd);
114 silc_schedule_task_del(client->schedule, ctx->task);
116 /* Start the key agreement */
117 silc_client_ftp_start_key_agreement(session, fd);
121 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
125 /* Create connection to server asynchronously */
126 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
130 /* Register task that will receive the async connect and will
132 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
133 silc_client_ftp_connected,
136 SILC_TASK_PRI_NORMAL);
137 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
144 silc_client_connect_to_client(SilcClient client,
145 SilcClientConnection conn, int port,
146 char *host, void *context)
148 SilcClientInternalConnectContext *ctx;
150 /* Allocate internal context for connection process. This is
151 needed as we are doing async connecting. */
152 ctx = silc_calloc(1, sizeof(*ctx));
153 ctx->client = client;
155 ctx->host = strdup(host);
158 ctx->context = context;
160 /* Do the actual connecting process */
161 return silc_client_connect_to_client_internal(ctx);
164 /* SFTP packet send callback. This will use preallocated buffer to avoid
165 reallocation of outgoing data buffer everytime. */
167 static void silc_client_ftp_send_packet(SilcBuffer packet, void *context)
169 SilcClientFtpSession session = (SilcClientFtpSession)context;
170 SilcClient client = session->client;
172 SILC_LOG_DEBUG(("Start"));
174 /* Allocate outgoing packet */
175 if (!session->packet)
176 session->packet = silc_buffer_alloc(1 + packet->len);
178 /* Enlarge outgoing packet if needed */
179 if (session->packet->truelen < 1 + packet->len)
180 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
183 silc_buffer_pull_tail(session->packet, 1 + packet->len);
184 silc_buffer_format(session->packet,
186 SILC_STR_UI_XNSTRING(packet->data, packet->len),
189 /* Send the packet immediately */
190 silc_client_packet_send(client, session->sock, SILC_PACKET_FTP, NULL,
191 0, NULL, NULL, session->packet->data,
192 session->packet->len, TRUE);
195 session->packet->data = session->packet->tail = session->packet->head;
196 session->packet->len = 0;
199 /* SFTP monitor callback for SFTP server. This reports the application
200 how the transmission is going along. This function is for the client
201 who made the file available for download. */
203 static void silc_client_ftp_monitor(SilcSFTP sftp,
204 SilcSFTPMonitors type,
205 const SilcSFTPMonitorData data,
208 SilcClientFtpSession session = (SilcClientFtpSession)context;
210 if (type == SILC_SFTP_MONITOR_READ) {
211 /* Call the monitor for application */
212 if (session->monitor)
213 (*session->monitor)(session->client, session->conn,
214 SILC_CLIENT_FILE_MONITOR_SEND,
216 data->offset, session->filesize,
217 session->client_entry, session->session_id,
218 session->filepath, session->monitor_context);
222 /* Returns the read data. This is the downloader's function (client side)
223 to receive the read data and read more until EOF is received from
224 the other side. This will also monitor the transmission and notify
227 static void silc_client_ftp_data(SilcSFTP sftp,
228 SilcSFTPStatus status,
229 const unsigned char *data,
233 SilcClientFtpSession session = (SilcClientFtpSession)context;
235 SILC_LOG_DEBUG(("Start"));
237 if (status == SILC_SFTP_STATUS_EOF) {
240 /* Close the handle */
241 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
242 session->read_handle = NULL;
244 /* Close the read file descriptor */
245 silc_file_close(session->fd);
249 if (status != SILC_SFTP_STATUS_OK) {
250 /* Call monitor callback */
251 if (session->monitor)
252 (*session->monitor)(session->client, session->conn,
253 SILC_CLIENT_FILE_MONITOR_ERROR,
254 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
255 SILC_CLIENT_FILE_NO_SUCH_FILE :
256 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
257 SILC_CLIENT_FILE_PERMISSION_DENIED :
258 SILC_CLIENT_FILE_ERROR), 0, 0,
259 session->client_entry, session->session_id,
260 session->filepath, session->monitor_context);
262 /* Close the handle */
263 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
264 session->read_handle = NULL;
266 /* Close the read file descriptor */
267 silc_file_close(session->fd);
271 /* Read more, until EOF is received */
272 session->read_offset += data_len;
273 silc_sftp_read(sftp, session->read_handle, session->read_offset,
274 SILC_PACKET_MAX_LEN - 1024,
275 silc_client_ftp_data, session);
277 /* Call monitor callback */
278 if (session->monitor)
279 (*session->monitor)(session->client, session->conn,
280 SILC_CLIENT_FILE_MONITOR_RECEIVE,
282 session->read_offset, session->filesize,
283 session->client_entry, session->session_id,
284 session->filepath, session->monitor_context);
286 /* Write the read data to the real file */
287 silc_file_write(session->fd, data, data_len);
290 /* Returns handle for the opened file. This is the downloader's function.
291 This will begin reading the data from the file. */
293 static void silc_client_ftp_open_handle(SilcSFTP sftp,
294 SilcSFTPStatus status,
295 SilcSFTPHandle handle,
298 SilcClientFtpSession session = (SilcClientFtpSession)context;
301 SILC_LOG_DEBUG(("Start"));
303 if (status != SILC_SFTP_STATUS_OK) {
304 /* Call monitor callback */
305 if (session->monitor)
306 (*session->monitor)(session->client, session->conn,
307 SILC_CLIENT_FILE_MONITOR_ERROR,
308 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
309 SILC_CLIENT_FILE_NO_SUCH_FILE :
310 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
311 SILC_CLIENT_FILE_PERMISSION_DENIED :
312 SILC_CLIENT_FILE_ERROR), 0, 0,
313 session->client_entry, session->session_id,
314 session->filepath, session->monitor_context);
318 /* Open the actual local file */
319 memset(path, 0, sizeof(path));
320 silc_snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
321 session->path : "", session->filepath);
322 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
323 if (session->fd < 0) {
324 /* Call monitor callback */
325 session->client->internal->ops->say(session->client, session->conn,
326 SILC_CLIENT_MESSAGE_ERROR,
327 "File `%s' open failed: %s",
331 if (session->monitor)
332 (*session->monitor)(session->client, session->conn,
333 SILC_CLIENT_FILE_MONITOR_ERROR,
334 SILC_CLIENT_FILE_PERMISSION_DENIED, 0, 0,
335 session->client_entry, session->session_id,
336 session->filepath, session->monitor_context);
340 session->read_handle = handle;
342 /* Now, start reading the file */
343 silc_sftp_read(sftp, session->read_handle, session->read_offset,
344 SILC_PACKET_MAX_LEN - 1024,
345 silc_client_ftp_data, session);
347 /* Call monitor callback */
348 if (session->monitor)
349 (*session->monitor)(session->client, session->conn,
350 SILC_CLIENT_FILE_MONITOR_RECEIVE,
352 session->read_offset, session->filesize,
353 session->client_entry, session->session_id,
354 session->filepath, session->monitor_context);
357 static void silc_client_ftp_ask_name(const char *filepath,
360 SilcClientFtpSession session = (SilcClientFtpSession)context;
361 SilcSFTPAttributesStruct attr;
362 char *remote_file = NULL;
364 SILC_LOG_DEBUG(("Start"));
367 remote_file = session->filepath;
368 session->filepath = NULL;
369 silc_free(session->path);
370 session->path = NULL;
371 session->filepath = strdup(filepath);
373 remote_file = strdup(session->filepath);
376 /* Now open the file */
377 memset(&attr, 0, sizeof(attr));
378 silc_sftp_open(session->sftp, remote_file, SILC_SFTP_FXF_READ, &attr,
379 silc_client_ftp_open_handle, session);
381 /* Close the directory handle */
382 silc_sftp_close(session->sftp, session->dir_handle, NULL, NULL);
383 session->dir_handle = NULL;
385 silc_free(remote_file);
388 /* Returns the file name available for download. This is the downloader's
391 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
392 SilcSFTPStatus status,
393 const SilcSFTPName name,
396 SilcClientFtpSession session = (SilcClientFtpSession)context;
398 SILC_LOG_DEBUG(("Start"));
400 if (status != SILC_SFTP_STATUS_OK) {
401 /* Call monitor callback */
402 if (session->monitor)
403 (*session->monitor)(session->client, session->conn,
404 SILC_CLIENT_FILE_MONITOR_ERROR,
405 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
406 SILC_CLIENT_FILE_NO_SUCH_FILE :
407 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
408 SILC_CLIENT_FILE_PERMISSION_DENIED :
409 SILC_CLIENT_FILE_ERROR), 0, 0,
410 session->client_entry, session->session_id,
411 session->filepath, session->monitor_context);
415 /* Save the important attributes like filename and file size */
416 session->filepath = strdup(name->filename[0]);
417 session->filesize = name->attrs[0]->size;
419 /* If the path was not provided, ask from application where to save the
421 if (!session->path && session->ask_name) {
422 session->ask_name(session->client, session->conn, session->session_id,
423 name->filename[0], silc_client_ftp_ask_name, session,
424 session->ask_name_context);
428 /* Start downloading immediately to current directory. */
429 silc_client_ftp_ask_name(NULL, session);
432 /* Returns the file handle after giving opendir command. This is the
433 downloader's function. */
435 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
436 SilcSFTPStatus status,
437 SilcSFTPHandle handle,
440 SilcClientFtpSession session = (SilcClientFtpSession)context;
442 SILC_LOG_DEBUG(("Start"));
444 if (status != SILC_SFTP_STATUS_OK) {
445 /* Call monitor callback */
446 if (session->monitor)
447 (*session->monitor)(session->client, session->conn,
448 SILC_CLIENT_FILE_MONITOR_ERROR,
449 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
450 SILC_CLIENT_FILE_NO_SUCH_FILE :
451 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
452 SILC_CLIENT_FILE_PERMISSION_DENIED :
453 SILC_CLIENT_FILE_ERROR), 0, 0,
454 session->client_entry, session->session_id,
455 session->filepath, session->monitor_context);
459 /* Now, read the directory */
460 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
461 session->dir_handle = handle;
464 /* SFTP version callback for SFTP client. This is the downloader's function
465 after initializing the SFTP connection to the remote client. This will
466 find out the filename available for download. */
468 static void silc_client_ftp_version(SilcSFTP sftp,
469 SilcSFTPStatus status,
470 SilcSFTPVersion version,
473 SilcClientFtpSession session = (SilcClientFtpSession)context;
475 SILC_LOG_DEBUG(("Start"));
477 if (status != SILC_SFTP_STATUS_OK) {
478 /* Call monitor callback */
479 if (session->monitor)
480 (*session->monitor)(session->client, session->conn,
481 SILC_CLIENT_FILE_MONITOR_ERROR,
482 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
483 SILC_CLIENT_FILE_NO_SUCH_FILE :
484 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
485 SILC_CLIENT_FILE_PERMISSION_DENIED :
486 SILC_CLIENT_FILE_ERROR), 0, 0,
487 session->client_entry, session->session_id,
488 session->filepath, session->monitor_context);
492 /* The SFTP session is open, now retrieve the info about available file. */
493 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
496 /* This callback is called after the key agreement protocol has been
497 performed. This calls the final completion callback for the application. */
499 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
501 SilcProtocol protocol = (SilcProtocol)context;
502 SilcClientKEInternalContext *ctx =
503 (SilcClientKEInternalContext *)protocol->context;
504 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
505 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
507 SILC_LOG_DEBUG(("Start"));
509 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
510 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
511 /* Call monitor callback */
512 if (session->monitor)
513 (*session->monitor)(session->client, session->conn,
514 SILC_CLIENT_FILE_MONITOR_ERROR,
515 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
516 session->client_entry, session->session_id,
517 session->filepath, session->monitor_context);
519 /* Error occured during protocol */
520 silc_ske_free_key_material(ctx->keymat);
524 /* Set keys into use */
525 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
526 ctx->ske->prop->cipher,
527 ctx->ske->prop->pkcs,
528 ctx->ske->prop->hash,
529 ctx->ske->prop->hmac,
530 ctx->ske->prop->group,
533 if (!session->server) {
534 /* If we are the SFTP client then start the SFTP session and retrieve
535 the info about the file available for download. */
536 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
537 session, silc_client_ftp_version,
540 /* Start SFTP server */
541 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
542 session, session->fs);
544 /* Monitor transmission */
545 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
546 silc_client_ftp_monitor, session);
549 /* Set this as active session */
550 conn->internal->active_session = session;
553 silc_ske_free_key_material(ctx->keymat);
555 silc_ske_free(ctx->ske);
556 silc_free(ctx->dest_id);
557 ctx->sock->protocol = NULL;
558 silc_socket_free(ctx->sock);
560 silc_protocol_free(protocol);
563 /* The downloader's function to start the key agreement protocol with the
564 remote client after we have connected to it. */
566 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
569 SilcClient client = session->client;
570 SilcClientKEInternalContext *proto_ctx;
571 SilcProtocol protocol;
572 SilcClientConnection conn;
575 SILC_LOG_DEBUG(("Start"));
577 /* Call monitor callback */
578 if (session->monitor)
579 (*session->monitor)(session->client, session->conn,
580 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
581 SILC_CLIENT_FILE_OK, 0, 0,
582 session->client_entry, session->session_id,
583 NULL, session->monitor_context);
585 /* Add new connection for this session */
586 conn = silc_client_add_connection(client, NULL, session->hostname,
587 session->port, session);
589 /* Allocate new socket connection object */
590 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
591 conn->sock->hostname = strdup(session->hostname);
592 conn->sock->port = silc_net_get_remote_port(sock);
593 session->sock = silc_socket_dup(conn->sock);
595 /* Allocate internal context for key exchange protocol. This is
596 sent as context for the protocol. */
597 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
598 proto_ctx->client = client;
599 proto_ctx->sock = silc_socket_dup(conn->sock);
600 proto_ctx->rng = client->rng;
601 proto_ctx->responder = FALSE;
602 proto_ctx->context = session;
603 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
604 proto_ctx->verify = silc_client_protocol_ke_verify_key;
606 /* Perform key exchange protocol. */
607 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
608 &protocol, (void *)proto_ctx,
609 silc_client_ftp_key_agreement_final);
610 conn->sock->protocol = protocol;
612 /* Register the connection for network input and output. This sets
613 that scheduler will listen for incoming packets for this connection
614 and sets that outgoing packets may be sent to this connection as well.
615 However, this doesn't set the scheduler for outgoing traffic, it will
616 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
617 later when outgoing data is available. */
618 context = (void *)client;
619 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
621 /* Execute the protocol */
622 silc_protocol_execute(protocol, client->schedule, 0, 0);
625 /* The remote client's (the client who made the file available for download)
626 function for accepting incoming connection. This will also start the
627 key agreement protocol with the other client. */
629 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
631 SilcClientFtpSession session = (SilcClientFtpSession)context;
632 SilcClient client = session->client;
633 SilcClientConnection conn;
634 SilcSocketConnection newsocket;
635 SilcClientKEInternalContext *proto_ctx;
638 SILC_LOG_DEBUG(("Start"));
640 sock = silc_net_accept_connection(session->listener);
642 /* Call monitor callback */
643 if (session->monitor)
644 (*session->monitor)(session->client, session->conn,
645 SILC_CLIENT_FILE_MONITOR_ERROR,
646 SILC_CLIENT_FILE_ERROR, 0, 0,
647 session->client_entry, session->session_id,
648 session->filepath, session->monitor_context);
652 /* Set socket options */
653 silc_net_set_socket_nonblock(sock);
654 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
656 /* Allocate new socket connection object */
657 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
659 /* Perform name and address lookups for the remote host. */
660 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
661 if (!newsocket->hostname && !newsocket->ip) {
662 /* Call monitor callback */
663 if (session->monitor)
664 (*session->monitor)(session->client, session->conn,
665 SILC_CLIENT_FILE_MONITOR_ERROR,
666 SILC_CLIENT_FILE_ERROR, 0, 0,
667 session->client_entry, session->session_id,
668 session->filepath, session->monitor_context);
671 if (!newsocket->hostname)
672 newsocket->hostname = strdup(newsocket->ip);
673 newsocket->port = silc_net_get_remote_port(sock);
675 /* Call monitor callback */
676 if (session->monitor)
677 (*session->monitor)(session->client, session->conn,
678 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
679 SILC_CLIENT_FILE_OK, 0, 0,
680 session->client_entry, session->session_id,
681 NULL, session->monitor_context);
683 /* Add new connection for this session */
684 conn = silc_client_add_connection(client, NULL, newsocket->hostname,
685 newsocket->port, session);
686 conn->sock = newsocket;
687 conn->sock->user_data = conn;
688 session->sock = silc_socket_dup(conn->sock);
690 /* Allocate internal context for key exchange protocol. This is
691 sent as context for the protocol. */
692 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
693 proto_ctx->client = client;
694 proto_ctx->sock = silc_socket_dup(conn->sock);
695 proto_ctx->rng = client->rng;
696 proto_ctx->responder = TRUE;
697 proto_ctx->context = session;
698 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
699 proto_ctx->verify = silc_client_protocol_ke_verify_key;
701 /* Prepare the connection for key exchange protocol. We allocate the
702 protocol but will not start it yet. The connector will be the
703 initiator of the protocol thus we will wait for initiation from
704 there before we start the protocol. */
705 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
706 &newsocket->protocol, proto_ctx,
707 silc_client_ftp_key_agreement_final);
709 /* Register the connection for network input and output. This sets
710 that scheduler will listen for incoming packets for this connection
711 and sets that outgoing packets may be sent to this connection as well.
712 However, this doesn't set the scheduler for outgoing traffic, it
713 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
714 later when outgoing data is available. */
715 context = (void *)client;
716 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
719 /* Free all file transfer sessions. */
721 void silc_client_ftp_free_sessions(SilcClient client,
722 SilcClientConnection conn)
724 if (conn->internal->ftp_sessions) {
725 SilcClientFtpSession session;
726 silc_dlist_start(conn->internal->ftp_sessions);
727 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
730 session->sock->user_data = NULL;
731 silc_client_ftp_session_free(session);
733 silc_dlist_del(conn->internal->ftp_sessions, session);
737 /* Free file transfer session by client entry. */
739 void silc_client_ftp_session_free_client(SilcClientConnection conn,
740 SilcClientEntry client_entry)
742 SilcClientFtpSession session;
744 if (!conn->internal->ftp_sessions)
747 /* Get the session */
748 silc_dlist_start(conn->internal->ftp_sessions);
749 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
751 if (session->client_entry == client_entry)
752 silc_client_ftp_session_free(session);
756 /* Free session resources. */
758 void silc_client_ftp_session_free(SilcClientFtpSession session)
760 SilcClientConnection conn;
762 SILC_LOG_DEBUG(("Free session"));
764 if (session->conn && session->conn->internal->ftp_sessions)
765 silc_dlist_del(session->conn->internal->ftp_sessions, session);
767 if (session->conn && session->conn->internal->active_session == session)
768 session->conn->internal->active_session = NULL;
772 silc_sftp_server_shutdown(session->sftp);
774 silc_sftp_client_shutdown(session->sftp);
778 silc_sftp_fs_memory_free(session->fs);
780 /* Destroy listener */
781 if (session->listener) {
782 silc_schedule_unset_listen_fd(session->client->schedule,
784 silc_net_close_connection(session->listener);
785 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
788 /* Destroy session connection */
790 silc_schedule_unset_listen_fd(session->client->schedule,
791 session->sock->sock);
792 silc_net_close_connection(session->sock->sock);
794 if (session->sock->user_data) {
795 conn = (SilcClientConnection)session->sock->user_data;
797 if (conn->internal->active_session == session)
798 conn->internal->active_session = NULL;
800 silc_client_close_connection_real(session->client, session->sock, conn);
802 silc_socket_free(session->sock);
807 silc_buffer_free(session->packet);
809 silc_free(session->hostname);
810 silc_free(session->filepath);
811 silc_free(session->path);
812 memset(session, 'F', sizeof(*session));
816 /* Sends a file indicated by the `filepath' to the remote client
817 indicated by the `client_entry'. This will negotiate a secret key
818 with the remote client before actually starting the transmission of
819 the file. The `monitor' callback will be called to monitor the
820 transmission of the file. */
823 silc_client_file_send(SilcClient client,
824 SilcClientEntry client_entry,
825 SilcClientConnectionParams *params,
826 SilcPublicKey public_key,
827 SilcPrivateKey private_key,
828 SilcClientFileMonitor monitor,
829 void *monitor_context,
830 const char *filepath,
831 SilcUInt32 *session_id)
833 SilcClientFtpSession session;
834 SilcBuffer keyagr, ftp;
835 char *filename, *path;
838 SILC_LOG_DEBUG(("File send request (file: %s), filepath"));
840 if (!client || !client_entry || !filepath)
841 return SILC_CLIENT_FILE_ERROR;
843 /* Check for existing session for `filepath'. */
844 silc_dlist_start(client->internal->ftp_sessions);
845 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
846 if (session->filepath && !strcmp(session->filepath, filepath) &&
847 session->client_entry == client_entry)
848 return SILC_CLIENT_FILE_ALREADY_STARTED;
851 /* See whether the file exists and can be opened */
852 fd = silc_file_open(filepath, O_RDONLY);
854 return SILC_CLIENT_FILE_NO_SUCH_FILE;
857 /* Add new session */
858 session = silc_calloc(1, sizeof(*session));
860 return SILC_CLIENT_FILE_ERROR;
861 session->session_id = ++client->internal->next_session_id;
862 session->client = client;
863 session->conn = conn;
864 session->server = TRUE;
865 session->client_entry = client_entry;
866 session->monitor = monitor;
867 session->monitor_context = monitor_context;
868 session->filepath = strdup(filepath);
869 silc_dlist_add(conn->internal->ftp_sessions, session);
871 silc_asprintf(&path, "file://%s", filepath);
873 /* Allocate memory filesystem and put the file to it */
874 if (strrchr(path, '/'))
875 filename = strrchr(path, '/') + 1;
877 filename = (char *)path;
878 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
879 SILC_SFTP_FS_PERM_EXEC);
880 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
883 session->filesize = silc_file_size(filepath);
885 /* If local IP is provided, create listener for incoming key exchange */
886 if (params && (params->local_ip || params->bind_ip)) {
887 ke = silc_calloc(1, sizeof(*ke));
889 completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
896 silc_net_tcp_create_listener(params->bind_ip ?
897 (const char **)¶ms->bind_ip :
898 (const char **)¶ms->local_ip,
899 1, params->local_port, FALSE, FALSE,
900 conn->internal->schedule,
901 silc_client_tcp_accept,
903 if (!session->listener) {
904 /* Could not create listener. Do the second best thing; send empty
905 key agreement packet and let the remote client provide the point
906 for the key exchange. */
907 SILC_LOG_DEBUG(("Could not create listener"));
908 silc_free(session->hostname);
909 session->hostname = NULL;
912 SILC_LOG_DEBUG(("Bound listener"));
913 session->bound = TRUE;
914 session->port = params->local_port;
915 if (!session->port) {
916 /* Get listener port */
918 ports = silc_net_listener_get_port(ke->tcp_listener, NULL);
919 session->port = ports[0];
925 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
927 /* Send the key agreement inside FTP packet */
928 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
930 ftp = silc_buffer_alloc(1 + keyagr->len);
931 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
932 silc_buffer_format(ftp,
934 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
936 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
937 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
938 ftp->data, ftp->len, FALSE);
940 silc_buffer_free(keyagr);
941 silc_buffer_free(ftp);
945 *session_id = session->session_id;
947 return SILC_CLIENT_FILE_OK;
950 /* Receives a file from a client indicated by the `client_entry'. The
951 `session_id' indicates the file transmission session and it has been
952 received in the `ftp' client operation function. This will actually
953 perform the key agreement protocol with the remote client before
954 actually starting the file transmission. The `monitor' callback
955 will be called to monitor the transmission. */
958 silc_client_file_receive(SilcClient client,
959 SilcClientConnection conn,
960 SilcClientFileMonitor monitor,
961 void *monitor_context,
963 SilcUInt32 session_id,
964 SilcClientFileAskName ask_name,
965 void *ask_name_context)
967 SilcClientFtpSession session;
968 SilcBuffer keyagr, ftp;
970 assert(client && conn);
972 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
974 /* Get the session */
975 silc_dlist_start(conn->internal->ftp_sessions);
976 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
978 if (session->session_id == session_id) {
983 if (session == SILC_LIST_END) {
984 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
985 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
988 /* See if we have this session running already */
989 if (session->sftp || session->listener) {
990 SILC_LOG_DEBUG(("Session already started"));
991 return SILC_CLIENT_FILE_ALREADY_STARTED;
994 session->monitor = monitor;
995 session->monitor_context = monitor_context;
996 session->ask_name = ask_name;
997 session->ask_name_context = ask_name_context;
998 session->conn = conn;
999 session->path = path ? strdup(path) : NULL;
1001 /* If the hostname and port already exists then the remote client did
1002 provide the connection point to us and we won't create listener, but
1003 create the connection ourselves. */
1004 if (session->hostname && session->port) {
1005 SILC_LOG_DEBUG(("Connecting to remote client"));
1006 if (silc_client_connect_to_client(client, conn, session->port,
1007 session->hostname, session) < 0)
1008 return SILC_CLIENT_FILE_ERROR;
1010 /* Add the listener for the key agreement */
1011 SILC_LOG_DEBUG(("Creating listener for file transfer"));
1012 session->listener = -1;
1013 silc_net_check_local_by_sock(conn->sock->sock, NULL, &session->hostname);
1014 if (session->hostname)
1015 session->listener = silc_net_create_server(0, session->hostname);
1016 if (session->listener < 0) {
1017 SILC_LOG_DEBUG(("Could not create listener"));
1018 session->listener = 0;
1019 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1020 "Cannot create listener on %s: %s",
1021 session->hostname, strerror(errno));
1022 return SILC_CLIENT_FILE_ERROR;
1024 session->port = silc_net_get_local_port(session->listener);
1025 silc_schedule_task_add(client->schedule, session->listener,
1026 silc_client_ftp_process_key_agreement, session,
1027 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
1029 /* Send the key agreement inside FTP packet */
1030 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
1031 keyagr = silc_key_agreement_payload_encode(session->hostname,
1033 ftp = silc_buffer_alloc(1 + keyagr->len);
1034 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
1035 silc_buffer_format(ftp,
1036 SILC_STR_UI_CHAR(1),
1037 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
1039 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
1040 session->client_entry->id,
1041 SILC_ID_CLIENT, NULL, NULL,
1042 ftp->data, ftp->len, FALSE);
1044 silc_buffer_free(keyagr);
1045 silc_buffer_free(ftp);
1048 return SILC_CLIENT_FILE_OK;
1051 SILC_TASK_CALLBACK(silc_client_file_close_final)
1053 silc_client_ftp_session_free(context);
1056 /* Closes file transmission session indicated by the `session_id'.
1057 If file transmission is being conducted it will be aborted
1058 automatically. This function is also used to close the session
1059 after successful file transmission. This function can be used
1060 also to reject incoming file transmission request. */
1062 SilcClientFileError silc_client_file_close(SilcClient client,
1063 SilcClientConnection conn,
1064 SilcUInt32 session_id)
1066 SilcClientFtpSession session;
1068 assert(client && conn);
1070 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
1072 /* Get the session */
1073 silc_dlist_start(conn->internal->ftp_sessions);
1074 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1076 if (session->session_id == session_id)
1080 if (session == SILC_LIST_END) {
1081 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1082 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1085 if (session->monitor)
1086 (*session->monitor)(session->client, session->conn,
1087 SILC_CLIENT_FILE_MONITOR_CLOSED,
1088 SILC_CLIENT_FILE_OK, 0, 0,
1089 session->client_entry, session->session_id,
1090 session->filepath, session->monitor_context);
1092 /* Destroy via timeout */
1093 silc_schedule_task_add(session->client->schedule, 0,
1094 silc_client_file_close_final, session,
1095 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1097 return SILC_CLIENT_FILE_OK;
1100 /************************** FTP Request Processing **************************/
1102 /* Client resolving callback. Continues with the FTP processing */
1104 static void silc_client_ftp_client_resolved(SilcClient client,
1105 SilcClientConnection conn,
1110 SilcFSMThread thread = context;
1111 SilcPacket packet = silc_fsm_get_state_context(thread);
1113 /* If no client found, ignore the packet, a silent error */
1115 silc_packet_free(packet);
1116 silc_fsm_finish(thread);
1120 /* Continue processing the packet */
1121 SILC_FSM_CALL_CONTINUE(context);
1124 /* Received file transfer packet. Only file transfer requests get here.
1125 The actual file transfer is handled by the SFTP library when we give it
1126 the packet stream wrapped into SilcStream context. */
1128 SILC_FSM_STATE(silc_client_ftp)
1130 SilcClientConnection conn = fsm_context;
1131 SilcClient client = conn->client;
1132 SilcPacket packet = state_context;
1133 SilcClientID remote_id;
1134 SilcClientEntry remote_client;
1135 SilcKeyAgreementPayload payload;
1141 SILC_LOG_DEBUG(("Process file transfer packet"));
1143 if (silc_buffer_len(&packet->buffer) < 1)
1146 /* We support file transfer type number 1 (== SFTP) */
1147 if (packet->buffer.data[0] != 0x01) {
1148 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
1149 packet->buffer.data[0]));
1153 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
1154 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
1155 SILC_LOG_DEBUG(("Invalid client ID"));
1159 /* Check whether we know this client already */
1160 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1161 if (!remote_client || !remote_client->nickname[0]) {
1162 /** Resolve client info */
1163 silc_client_unref_client(client, conn, remote_client);
1164 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1165 client, conn, &remote_id, NULL,
1166 silc_client_ftp_client_resolved,
1172 silc_dlist_start(conn->internal->ftp_sessions);
1173 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1175 if (session->client_entry == client_entry &&
1176 (!session->server || !session->bound))
1180 /* Parse the key agreement payload */
1181 payload = silc_key_agreement_payload_parse(packet->buffer->data + 1,
1182 packet->buffer->len - 1);
1184 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1188 hostname = silc_key_agreement_get_hostname(payload);
1189 port = silc_key_agreement_get_port(payload);
1195 /* If session doesn't exist, we create one and let applicationi know about
1196 incoming file transfer request. If session exists, but we are responder
1197 it means that the remote sent another request and user hasn't even
1198 accepted the first one yet. We assume this session is new session
1200 if (session == SILC_LIST_END || (!hostname && !port) ||
1201 (session && session->server == FALSE)) {
1202 /* No session found, create one and let the application know about
1203 incoming file transfer request. */
1204 SILC_LOG_DEBUG(("New file transfer session ID: %d",
1205 conn->internal->next_session_id + 1));
1207 /* Add new session */
1208 session = silc_calloc(1, sizeof(*session));
1209 session->session_id = ++conn->internal->next_session_id;
1210 session->client = client;
1211 session->conn = conn;
1212 session->client_entry = client_entry;
1213 silc_dlist_add(conn->internal->ftp_sessions, session);
1215 if (hostname && port) {
1216 session->hostname = strdup(hostname);
1217 session->port = port;
1220 /* Let the application know */
1221 client->internal->ops->ftp(client, conn, client_entry,
1222 session->session_id, hostname, port);
1227 /* Session exists, continue with key agreement protocol. */
1228 SILC_LOG_DEBUG(("Session ID %d exists, connecting to remote client",
1229 session->session_id));
1231 session->hostname = strdup(hostname);
1232 session->port = port;
1234 if (silc_client_connect_to_client(client, conn, port,
1235 hostname, session) < 0) {
1236 /* Call monitor callback */
1237 if (session->monitor)
1238 (*session->monitor)(session->client, session->conn,
1239 SILC_CLIENT_FILE_MONITOR_ERROR,
1240 SILC_CLIENT_FILE_ERROR, 0, 0,
1241 session->client_entry, session->session_id,
1242 session->filepath, session->monitor_context);
1248 silc_packet_free(packet);
1249 return SILC_FSM_FINISH;