5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 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.
21 #include "clientlibincludes.h"
22 #include "client_internal.h"
25 silc_client_connect_to_client(SilcClient client,
26 SilcClientConnection conn, int port,
27 char *host, void *context);
29 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
30 SILC_TASK_CALLBACK(silc_client_ftp_connected);
31 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
34 /* File transmission session */
35 struct SilcClientFtpSessionStruct {
38 SilcClientConnection conn;
39 SilcClientEntry client_entry;
41 SilcSocketConnection sock;
48 SilcClientFileMonitor monitor;
49 void *monitor_context;
53 SilcSFTPFilesystem fs;
56 SilcSFTPHandle dir_handle;
57 SilcSFTPHandle read_handle;
63 SILC_TASK_CALLBACK(silc_client_ftp_connected)
65 SilcClientInternalConnectContext *ctx =
66 (SilcClientInternalConnectContext *)context;
67 SilcClient client = ctx->client;
68 SilcClientConnection conn = ctx->conn;
69 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
70 int opt, opt_len = sizeof(opt);
72 SILC_LOG_DEBUG(("Start"));
74 /* Check the socket status as it might be in error */
75 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
78 /* Connection failed but lets try again */
79 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
80 "Could not connect to client %s: %s",
81 ctx->host, strerror(opt));
82 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
83 "Connecting to port %d of client %s resumed",
84 ctx->port, ctx->host);
86 /* Unregister old connection try */
87 silc_schedule_unset_listen_fd(client->schedule, fd);
88 silc_net_close_connection(fd);
89 silc_schedule_task_del(client->schedule, ctx->task);
92 silc_client_connect_to_client_internal(ctx);
95 /* Connection failed and we won't try anymore */
96 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
97 "Could not connect to client %s: %s",
98 ctx->host, strerror(opt));
99 silc_schedule_unset_listen_fd(client->schedule, fd);
100 silc_net_close_connection(fd);
101 silc_schedule_task_del(client->schedule, ctx->task);
103 silc_client_ftp_session_free(session);
108 silc_schedule_unset_listen_fd(client->schedule, fd);
109 silc_schedule_task_del(client->schedule, ctx->task);
111 /* Start the key agreement */
112 silc_client_ftp_start_key_agreement(session, fd);
116 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
120 /* Create connection to server asynchronously */
121 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
125 /* Register task that will receive the async connect and will
127 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
128 silc_client_ftp_connected,
131 SILC_TASK_PRI_NORMAL);
132 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
138 silc_client_connect_to_client(SilcClient client,
139 SilcClientConnection conn, int port,
140 char *host, void *context)
142 SilcClientInternalConnectContext *ctx;
144 /* Allocate internal context for connection process. This is
145 needed as we are doing async connecting. */
146 ctx = silc_calloc(1, sizeof(*ctx));
147 ctx->client = client;
149 ctx->host = strdup(host);
152 ctx->context = context;
154 /* Do the actual connecting process */
155 return silc_client_connect_to_client_internal(ctx);
158 /* SFTP packet send callback. This will use preallocated buffer to avoid
159 reallocation of outgoing data buffer everytime. */
161 static void silc_client_ftp_send_packet(SilcSocketConnection sock,
162 SilcBuffer packet, void *context)
164 SilcClientFtpSession session = (SilcClientFtpSession)context;
165 SilcClient client = session->client;
167 SILC_LOG_DEBUG(("Start"));
169 /* Allocate outgoing packet */
170 if (!session->packet)
171 session->packet = silc_buffer_alloc(1 + packet->len);
173 /* Enlarge outgoing packet if needed */
174 if (session->packet->truelen < 1 + packet->len)
175 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
178 silc_buffer_pull_tail(session->packet, 1 + packet->len);
179 silc_buffer_format(session->packet,
181 SILC_STR_UI_XNSTRING(packet->data, packet->len),
184 /* Send the packet immediately */
185 silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
186 session->packet->data, session->packet->len, TRUE);
188 silc_buffer_clear(session->packet);
191 /* SFTP monitor callback for SFTP server. This reports the application
192 how the transmission is going along. This function is for the client
193 who made the file available for download. */
195 static void silc_client_ftp_monitor(SilcSFTP sftp,
196 SilcSFTPMonitors type,
197 const SilcSFTPMonitorData data,
200 SilcClientFtpSession session = (SilcClientFtpSession)context;
202 if (type == SILC_SFTP_MONITOR_READ) {
203 /* Call the monitor for application */
204 if (session->monitor)
205 (*session->monitor)(session->client, session->conn,
206 SILC_CLIENT_FILE_MONITOR_SEND,
207 data->offset, session->filesize,
208 session->client_entry, session->session_id,
209 session->filepath, session->monitor_context);
213 /* Returns the read data. This is the downloader's function (client side)
214 to receive the read data and read more until EOF is received from
215 the other side. This will also monitor the transmission and notify
218 static void silc_client_ftp_data(SilcSFTP sftp,
219 SilcSFTPStatus status,
220 const unsigned char *data,
224 SilcClientFtpSession session = (SilcClientFtpSession)context;
226 SILC_LOG_DEBUG(("Start"));
228 if (status == SILC_SFTP_STATUS_EOF) {
231 /* Close the handle */
232 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
233 session->read_handle = NULL;
235 /* Close the read file descriptor */
236 silc_file_close(session->fd);
240 if (status != SILC_SFTP_STATUS_OK) {
241 /* Call monitor callback */
242 if (session->monitor)
243 (*session->monitor)(session->client, session->conn,
244 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
245 session->client_entry, session->session_id,
246 session->filepath, session->monitor_context);
248 /* Close the handle */
249 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
250 session->read_handle = NULL;
252 /* Close the read file descriptor */
253 silc_file_close(session->fd);
257 /* Read more, until EOF is received */
258 session->read_offset += data_len;
259 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
260 silc_client_ftp_data, session);
262 /* Call monitor callback */
263 if (session->monitor)
264 (*session->monitor)(session->client, session->conn,
265 SILC_CLIENT_FILE_MONITOR_RECEIVE,
266 session->read_offset, session->filesize,
267 session->client_entry, session->session_id,
268 session->filepath, session->monitor_context);
270 /* Write the read data to the real file */
271 silc_file_write(session->fd, data, data_len);
274 /* Returns handle for the opened file. This is the downloader's function.
275 This will begin reading the data from the file. */
277 static void silc_client_ftp_open_handle(SilcSFTP sftp,
278 SilcSFTPStatus status,
279 SilcSFTPHandle handle,
282 SilcClientFtpSession session = (SilcClientFtpSession)context;
284 SILC_LOG_DEBUG(("Start"));
286 if (status != SILC_SFTP_STATUS_OK) {
287 /* Call monitor callback */
288 if (session->monitor)
289 (*session->monitor)(session->client, session->conn,
290 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
291 session->client_entry, session->session_id,
292 session->filepath, session->monitor_context);
296 /* Open the actual local file */
297 session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT);
298 if (session->fd < 0) {
299 /* Call monitor callback */
300 if (session->monitor)
301 (*session->monitor)(session->client, session->conn,
302 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
303 session->client_entry, session->session_id,
304 session->filepath, session->monitor_context);
308 session->read_handle = handle;
310 /* Now, start reading the file */
311 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
312 silc_client_ftp_data, session);
314 /* Call monitor callback */
315 if (session->monitor)
316 (*session->monitor)(session->client, session->conn,
317 SILC_CLIENT_FILE_MONITOR_RECEIVE,
318 session->read_offset, session->filesize,
319 session->client_entry, session->session_id,
320 session->filepath, session->monitor_context);
323 /* Returns the file name available for download. This is the downloader's
326 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
327 SilcSFTPStatus status,
328 const SilcSFTPName name,
331 SilcClientFtpSession session = (SilcClientFtpSession)context;
332 SilcSFTPAttributesStruct attr;
334 SILC_LOG_DEBUG(("Start"));
336 if (status != SILC_SFTP_STATUS_OK) {
337 /* Call monitor callback */
338 if (session->monitor)
339 (*session->monitor)(session->client, session->conn,
340 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
341 session->client_entry, session->session_id,
342 session->filepath, session->monitor_context);
346 /* Now open the file */
347 memset(&attr, 0, sizeof(attr));
348 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
349 silc_client_ftp_open_handle, session);
351 /* Save the important attributes */
352 session->filepath = strdup(name->filename[0]);
353 session->filesize = name->attrs[0]->size;
355 /* Close the directory handle */
356 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
357 session->dir_handle = NULL;
360 /* Returns the file handle after giving opendir command. This is the
361 downloader's function. */
363 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
364 SilcSFTPStatus status,
365 SilcSFTPHandle handle,
368 SilcClientFtpSession session = (SilcClientFtpSession)context;
370 SILC_LOG_DEBUG(("Start"));
372 if (status != SILC_SFTP_STATUS_OK) {
373 /* Call monitor callback */
374 if (session->monitor)
375 (*session->monitor)(session->client, session->conn,
376 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
377 session->client_entry, session->session_id,
378 session->filepath, session->monitor_context);
382 /* Now, read the directory */
383 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
384 session->dir_handle = handle;
387 /* SFTP version callback for SFTP client. This is the downloader's function
388 after initializing the SFTP connection to the remote client. This will
389 find out the filename available for download. */
391 static void silc_client_ftp_version(SilcSFTP sftp,
392 SilcSFTPStatus status,
393 SilcSFTPVersion version,
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, 0, 0,
405 session->client_entry, session->session_id,
406 session->filepath, session->monitor_context);
410 /* The SFTP session is open, now retrieve the info about available file. */
411 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
414 /* This callback is called after the key agreement protocol has been
415 performed. This calls the final completion callback for the application. */
417 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
419 SilcProtocol protocol = (SilcProtocol)context;
420 SilcClientKEInternalContext *ctx =
421 (SilcClientKEInternalContext *)protocol->context;
422 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
423 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
425 SILC_LOG_DEBUG(("Start"));
427 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
428 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
429 /* Error occured during protocol */
430 silc_ske_free_key_material(ctx->keymat);
434 /* Set keys into use */
435 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
436 ctx->ske->prop->cipher,
437 ctx->ske->prop->pkcs,
438 ctx->ske->prop->hash,
439 ctx->ske->prop->hmac,
440 ctx->ske->prop->group,
443 /* If we are the SFTP client then start the SFTP session and retrieve
444 the info about the file available for download. */
445 if (!session->server) {
446 session->sftp = silc_sftp_client_start(conn->sock,
447 silc_client_ftp_send_packet,
449 silc_client_ftp_version, session);
452 /* Set this as active session */
453 conn->active_session = session;
456 silc_ske_free_key_material(ctx->keymat);
458 silc_ske_free(ctx->ske);
459 silc_free(ctx->dest_id);
460 ctx->sock->protocol = NULL;
461 silc_socket_free(ctx->sock);
463 silc_protocol_free(protocol);
466 /* The downloader's function to start the key agreement protocol with the
467 remote client after we have connected to it. */
469 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
472 SilcClient client = session->client;
473 SilcClientKEInternalContext *proto_ctx;
474 SilcProtocol protocol;
475 SilcClientConnection conn;
478 SILC_LOG_DEBUG(("Start"));
480 /* Call monitor callback */
481 if (session->monitor)
482 (*session->monitor)(session->client, session->conn,
483 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
484 session->client_entry, session->session_id,
485 NULL, session->monitor_context);
487 /* Add new connection for this session */
488 conn = silc_client_add_connection(client, session->hostname,
489 session->port, session);
491 /* Allocate new socket connection object */
492 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
493 conn->sock->hostname = strdup(session->hostname);
494 conn->sock->port = silc_net_get_remote_port(sock);
495 session->sock = silc_socket_dup(conn->sock);
497 /* Allocate the SFTP */
498 if (session->server) {
499 session->sftp = silc_sftp_server_start(conn->sock,
500 silc_client_ftp_send_packet,
501 session, session->fs);
503 /* Monitor transmission */
504 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
505 silc_client_ftp_monitor, session);
508 /* Allocate internal context for key exchange protocol. This is
509 sent as context for the protocol. */
510 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
511 proto_ctx->client = client;
512 proto_ctx->sock = silc_socket_dup(conn->sock);
513 proto_ctx->rng = client->rng;
514 proto_ctx->responder = FALSE;
515 proto_ctx->context = session;
516 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
517 proto_ctx->verify = silc_client_protocol_ke_verify_key;
519 /* Perform key exchange protocol. */
520 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
521 &protocol, (void *)proto_ctx,
522 silc_client_ftp_key_agreement_final);
523 conn->sock->protocol = protocol;
525 /* Register the connection for network input and output. This sets
526 that scheduler will listen for incoming packets for this connection
527 and sets that outgoing packets may be sent to this connection as well.
528 However, this doesn't set the scheduler for outgoing traffic, it will
529 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
530 later when outgoing data is available. */
531 context = (void *)client;
532 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
534 /* Execute the protocol */
535 silc_protocol_execute(protocol, client->schedule, 0, 0);
538 /* The remote client's (the client who made the file available for download)
539 function for accepting incoming connection. This will also start the
540 key agreement protocol with the other client. */
542 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
544 SilcClientFtpSession session = (SilcClientFtpSession)context;
545 SilcClient client = session->client;
546 SilcClientConnection conn;
547 SilcSocketConnection newsocket;
548 SilcClientKEInternalContext *proto_ctx;
551 SILC_LOG_DEBUG(("Start"));
553 sock = silc_net_accept_connection(session->listener);
555 /* Call monitor callback */
556 if (session->monitor)
557 (*session->monitor)(session->client, session->conn,
558 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
559 session->client_entry, session->session_id,
560 session->filepath, session->monitor_context);
564 /* Set socket options */
565 silc_net_set_socket_nonblock(sock);
566 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
568 /* Allocate new socket connection object */
569 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
571 /* Perform name and address lookups for the remote host. */
572 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
573 if (!newsocket->hostname && !newsocket->ip) {
574 /* Call monitor callback */
575 if (session->monitor)
576 (*session->monitor)(session->client, session->conn,
577 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
578 session->client_entry, session->session_id,
579 session->filepath, session->monitor_context);
582 if (!newsocket->hostname)
583 newsocket->hostname = strdup(newsocket->ip);
584 newsocket->port = silc_net_get_remote_port(sock);
586 /* Call monitor callback */
587 if (session->monitor)
588 (*session->monitor)(session->client, session->conn,
589 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
590 session->client_entry, session->session_id,
591 NULL, session->monitor_context);
593 /* Add new connection for this session */
594 conn = silc_client_add_connection(client, newsocket->hostname,
595 newsocket->port, session);
596 conn->sock = newsocket;
597 conn->sock->user_data = conn;
598 session->sock = silc_socket_dup(conn->sock);
600 /* Allocate internal context for key exchange protocol. This is
601 sent as context for the protocol. */
602 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
603 proto_ctx->client = client;
604 proto_ctx->sock = silc_socket_dup(conn->sock);
605 proto_ctx->rng = client->rng;
606 proto_ctx->responder = TRUE;
607 proto_ctx->context = session;
608 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
609 proto_ctx->verify = silc_client_protocol_ke_verify_key;
611 /* Prepare the connection for key exchange protocol. We allocate the
612 protocol but will not start it yet. The connector will be the
613 initiator of the protocol thus we will wait for initiation from
614 there before we start the protocol. */
615 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
616 &newsocket->protocol, proto_ctx,
617 silc_client_ftp_key_agreement_final);
619 /* Register the connection for network input and output. This sets
620 that scheduler will listen for incoming packets for this connection
621 and sets that outgoing packets may be sent to this connection as well.
622 However, this doesn't set the scheduler for outgoing traffic, it
623 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
624 later when outgoing data is available. */
625 context = (void *)client;
626 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
629 /* Free all file transfer sessions. */
631 void silc_client_ftp_free_sessions(SilcClient client,
632 SilcClientConnection conn)
634 if (conn->ftp_sessions) {
635 SilcClientFtpSession session;
636 silc_dlist_start(conn->ftp_sessions);
637 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
638 session->sock->user_data = NULL;
639 silc_client_ftp_session_free(session);
641 silc_dlist_del(conn->ftp_sessions, session);
642 silc_dlist_uninit(conn->ftp_sessions);
646 /* Free file transfer session by client entry. */
648 void silc_client_ftp_session_free_client(SilcClientConnection conn,
649 SilcClientEntry client_entry)
651 SilcClientFtpSession session;
653 if (!conn->ftp_sessions)
656 /* Get the session */
657 silc_dlist_start(conn->ftp_sessions);
658 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
659 if (session->client_entry == client_entry) {
660 session->sock->user_data = NULL;
661 silc_client_ftp_session_free(session);
667 /* Free session resources. */
669 void silc_client_ftp_session_free(SilcClientFtpSession session)
671 SilcClientConnection conn;
673 SILC_LOG_DEBUG(("Free session"));
675 silc_dlist_del(session->conn->ftp_sessions, session);
679 silc_sftp_server_shutdown(session->sftp);
681 silc_sftp_client_shutdown(session->sftp);
685 silc_sftp_fs_memory_free(session->fs);
687 /* Destroy listener */
688 if (session->listener) {
689 silc_schedule_unset_listen_fd(session->client->schedule,
691 silc_net_close_connection(session->listener);
692 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
695 /* Destroy session connection */
697 silc_schedule_unset_listen_fd(session->client->schedule,
698 session->sock->sock);
699 silc_net_close_connection(session->sock->sock);
701 if (session->sock->user_data) {
702 conn = (SilcClientConnection)session->sock->user_data;
704 if (conn->active_session == session)
705 conn->active_session = NULL;
707 silc_client_close_connection(session->client, session->sock, conn);
709 silc_socket_free(session->sock);
714 silc_buffer_free(session->packet);
716 silc_free(session->hostname);
717 silc_free(session->filepath);
721 /* Sends a file indicated by the `filepath' to the remote client
722 indicated by the `client_entry'. This will negotiate a secret key
723 with the remote client before actually starting the transmission of
724 the file. The `monitor' callback will be called to monitor the
725 transmission of the file.
727 This returns a file session ID for the file transmission. It can
728 be used to close the session (and abort the file transmission) by
729 calling the silc_client_file_close function. The session ID is
730 also returned in the `monitor' callback. This returns 0 if the
731 file indicated by the `filepath' is being transmitted to the remote
732 client indicated by the `client_entry', already. */
734 uint32 silc_client_file_send(SilcClient client,
735 SilcClientConnection conn,
736 SilcClientFileMonitor monitor,
737 void *monitor_context,
738 SilcClientEntry client_entry,
739 const char *filepath)
741 SilcClientFtpSession session;
742 SilcBuffer keyagr, ftp;
743 char *filename, *path;
745 SILC_LOG_DEBUG(("Start"));
747 /* Check for existing session for `filepath'. */
748 silc_dlist_start(conn->ftp_sessions);
749 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
750 if (!strcmp(session->filepath, filepath) &&
751 session->client_entry == client_entry)
755 /* Add new session */
756 session = silc_calloc(1, sizeof(*session));
757 session->session_id = ++conn->next_session_id;
758 session->client = client;
759 session->conn = conn;
760 session->client_entry = client_entry;
761 session->monitor = monitor;
762 session->monitor_context = monitor_context;
763 session->filepath = strdup(filepath);
764 session->server = TRUE;
765 silc_dlist_add(conn->ftp_sessions, session);
767 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
768 strcat(path, "file://");
769 strncat(path, filepath, strlen(filepath));
771 /* Allocate memory filesystem and put the file to it */
772 if (strrchr(path, '/'))
773 filename = strrchr(path, '/') + 1;
775 filename = (char *)path;
776 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
777 SILC_SFTP_FS_PERM_EXEC);
778 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
781 session->filesize = silc_file_size(filepath);
783 /* Send the key agreement inside FTP packet */
784 keyagr = silc_key_agreement_payload_encode(NULL, 0);
786 ftp = silc_buffer_alloc(1 + keyagr->len);
787 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
788 silc_buffer_format(ftp,
790 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
792 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
793 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
794 ftp->data, ftp->len, FALSE);
796 silc_buffer_free(keyagr);
797 silc_buffer_free(ftp);
800 return session->session_id;
803 /* Receives a file from a client indicated by the `client_entry'. The
804 `session_id' indicates the file transmission session and it has been
805 received in the `ftp' client operation function. This will actually
806 perform the key agreement protocol with the remote client before
807 actually starting the file transmission. The `monitor' callback
808 will be called to monitor the transmission. */
811 silc_client_file_receive(SilcClient client,
812 SilcClientConnection conn,
813 SilcClientFileMonitor monitor,
814 void *monitor_context,
815 SilcClientEntry client_entry,
818 SilcClientFtpSession session;
819 SilcBuffer keyagr, ftp;
821 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
823 /* Get the session */
824 silc_dlist_start(conn->ftp_sessions);
825 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
826 if (session->session_id == session_id) {
831 if (session == SILC_LIST_END) {
832 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
833 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
836 /* See if we have this session running already */
837 if (session->sftp || session->listener) {
838 SILC_LOG_DEBUG(("Session already started"));
839 return SILC_CLIENT_FILE_ALREADY_STARTED;
842 session->monitor = monitor;
843 session->monitor_context = monitor_context;
844 session->client_entry = client_entry;
845 session->conn = conn;
847 /* If the hostname and port already exists then the remote client did
848 provide the connection point to us and we won't create listener, but
849 create the connection ourselves. */
850 if (session->hostname && session->port) {
851 if (silc_client_connect_to_client(client, conn, session->port,
852 session->hostname, session) < 0)
853 return SILC_CLIENT_FILE_ERROR;
855 /* Add the listener for the key agreement */
856 session->hostname = silc_net_localip();
857 session->listener = silc_net_create_server(0, session->hostname);
858 if (session->listener < 0) {
859 SILC_LOG_DEBUG(("Could not create listener"));
860 return SILC_CLIENT_FILE_ERROR;
862 session->port = silc_net_get_local_port(session->listener);
863 silc_schedule_task_add(client->schedule, session->listener,
864 silc_client_ftp_process_key_agreement, session,
865 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
867 /* Send the key agreement inside FTP packet */
868 keyagr = silc_key_agreement_payload_encode(session->hostname,
870 ftp = silc_buffer_alloc(1 + keyagr->len);
871 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
872 silc_buffer_format(ftp,
874 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
876 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
877 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
878 ftp->data, ftp->len, FALSE);
880 silc_buffer_free(keyagr);
881 silc_buffer_free(ftp);
884 return SILC_CLIENT_FILE_OK;
887 /* Closes file transmission session indicated by the `session_id'.
888 If file transmission is being conducted it will be aborted
889 automatically. This function is also used to close the session
890 after successful file transmission. This function can be used
891 also to reject incoming file transmission request. */
893 SilcClientFileError silc_client_file_close(SilcClient client,
894 SilcClientConnection conn,
897 SilcClientFtpSession session;
899 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
901 /* Get the session */
902 silc_dlist_start(conn->ftp_sessions);
903 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
904 if (session->session_id == session_id) {
909 if (session == SILC_LIST_END) {
910 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
911 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
914 silc_client_ftp_session_free(session);
916 return SILC_CLIENT_FILE_OK;
919 /* Callback called after remote client information has been resolved.
920 This will try to find existing session for the client entry. If found
921 then continue with the key agreement protocol. If not then it means
922 this is a file transfer request and we let the application know. */
924 static void silc_client_ftp_resolve_cb(SilcClient client,
925 SilcClientConnection conn,
926 SilcClientEntry *clients,
927 uint32 clients_count,
930 SilcPacketContext *packet = (SilcPacketContext *)context;
931 SilcClientFtpSession session;
932 SilcKeyAgreementPayload payload = NULL;
933 SilcClientEntry client_entry;
937 SILC_LOG_DEBUG(("Start"));
942 client_entry = clients[0];
944 silc_dlist_start(conn->ftp_sessions);
945 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
946 if (session->client_entry == client_entry)
950 /* Parse the key agreement payload */
951 payload = silc_key_agreement_payload_parse(packet->buffer);
955 hostname = silc_key_agreement_get_hostname(payload);
956 port = silc_key_agreement_get_port(payload);
958 if (session == SILC_LIST_END) {
959 /* No session found, create one and let the application know about
960 incoming file transfer request. */
962 /* Add new session */
963 session = silc_calloc(1, sizeof(*session));
964 session->session_id = ++conn->next_session_id;
965 session->client = client;
966 session->conn = conn;
967 silc_dlist_add(conn->ftp_sessions, session);
969 /* Let the application know */
970 client->ops->ftp(client, conn, client_entry,
971 session->session_id, hostname, port);
973 if (hostname && port) {
974 session->hostname = strdup(hostname);
975 session->port = port;
984 session->hostname = strdup(hostname);
985 session->port = port;
987 /* Session exists, continue with key agreement protocol. */
988 if (silc_client_connect_to_client(client, conn, port,
989 hostname, session) < 0) {
990 /* Call monitor callback */
991 if (session->monitor)
992 (*session->monitor)(session->client, session->conn,
993 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
994 session->client_entry, session->session_id,
995 session->filepath, session->monitor_context);
1000 silc_key_agreement_payload_free(payload);
1001 silc_packet_context_free(packet);
1004 /* Called when file transfer packet is received. This will parse the
1005 packet and give it to the file transfer protocol. */
1007 void silc_client_ftp(SilcClient client,
1008 SilcSocketConnection sock,
1009 SilcPacketContext *packet)
1011 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1015 SILC_LOG_DEBUG(("Start"));
1017 /* Parse the payload */
1018 ret = silc_buffer_unformat(packet->buffer,
1019 SILC_STR_UI_CHAR(&type),
1024 /* We support only type number 1 (== SFTP) */
1028 silc_buffer_pull(packet->buffer, 1);
1030 /* If we have active FTP session then give the packet directly to the
1031 protocol processor. */
1032 if (conn->active_session) {
1033 /* Give it to the SFTP */
1034 if (conn->active_session->server)
1035 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1038 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1041 /* We don't have active session, resolve the remote client information
1042 and then try to find the correct session. */
1043 SilcClientID *remote_id;
1045 if (packet->src_id_type != SILC_ID_CLIENT)
1048 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1053 /* Resolve the client */
1054 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1055 silc_client_ftp_resolve_cb,
1056 silc_packet_context_dup(packet));
1057 silc_free(remote_id);