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 "silcincludes.h"
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;
55 SilcSFTPFilesystem fs;
56 unsigned int server : 1;
57 unsigned int bound : 1;
59 SilcSFTPHandle dir_handle;
60 SilcSFTPHandle read_handle;
62 SilcUInt64 read_offset;
66 SILC_TASK_CALLBACK(silc_client_ftp_connected)
68 SilcClientInternalConnectContext *ctx =
69 (SilcClientInternalConnectContext *)context;
70 SilcClient client = ctx->client;
71 SilcClientConnection conn = ctx->conn;
72 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
73 int opt, opt_len = sizeof(opt);
75 SILC_LOG_DEBUG(("Start"));
77 /* Check the socket status as it might be in error */
78 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
81 /* Connection failed but lets try again */
82 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
83 "Could not connect to client %s: %s",
84 ctx->host, strerror(opt));
85 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
86 "Connecting to port %d of client %s resumed",
87 ctx->port, ctx->host);
89 /* Unregister old connection try */
90 silc_schedule_unset_listen_fd(client->schedule, fd);
91 silc_net_close_connection(fd);
92 silc_schedule_task_del(client->schedule, ctx->task);
95 silc_client_connect_to_client_internal(ctx);
98 /* Connection failed and we won't try anymore */
99 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
100 "Could not connect to client %s: %s",
101 ctx->host, strerror(opt));
102 silc_schedule_unset_listen_fd(client->schedule, fd);
103 silc_net_close_connection(fd);
104 silc_schedule_task_del(client->schedule, ctx->task);
106 silc_client_ftp_session_free(session);
111 silc_schedule_unset_listen_fd(client->schedule, fd);
112 silc_schedule_task_del(client->schedule, ctx->task);
114 /* Start the key agreement */
115 silc_client_ftp_start_key_agreement(session, fd);
119 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
123 /* Create connection to server asynchronously */
124 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
128 /* Register task that will receive the async connect and will
130 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
131 silc_client_ftp_connected,
134 SILC_TASK_PRI_NORMAL);
135 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
142 silc_client_connect_to_client(SilcClient client,
143 SilcClientConnection conn, int port,
144 char *host, void *context)
146 SilcClientInternalConnectContext *ctx;
148 /* Allocate internal context for connection process. This is
149 needed as we are doing async connecting. */
150 ctx = silc_calloc(1, sizeof(*ctx));
151 ctx->client = client;
153 ctx->host = strdup(host);
156 ctx->context = context;
158 /* Do the actual connecting process */
159 return silc_client_connect_to_client_internal(ctx);
162 /* SFTP packet send callback. This will use preallocated buffer to avoid
163 reallocation of outgoing data buffer everytime. */
165 static void silc_client_ftp_send_packet(SilcBuffer packet, void *context)
167 SilcClientFtpSession session = (SilcClientFtpSession)context;
168 SilcClient client = session->client;
170 SILC_LOG_DEBUG(("Start"));
172 /* Allocate outgoing packet */
173 if (!session->packet)
174 session->packet = silc_buffer_alloc(1 + packet->len);
176 /* Enlarge outgoing packet if needed */
177 if (session->packet->truelen < 1 + packet->len)
178 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
181 silc_buffer_pull_tail(session->packet, 1 + packet->len);
182 silc_buffer_format(session->packet,
184 SILC_STR_UI_XNSTRING(packet->data, packet->len),
187 /* Send the packet immediately */
188 silc_client_packet_send(client, session->sock, SILC_PACKET_FTP, NULL,
189 0, NULL, NULL, session->packet->data,
190 session->packet->len, TRUE);
193 session->packet->data = session->packet->tail = session->packet->head;
194 session->packet->len = 0;
197 /* SFTP monitor callback for SFTP server. This reports the application
198 how the transmission is going along. This function is for the client
199 who made the file available for download. */
201 static void silc_client_ftp_monitor(SilcSFTP sftp,
202 SilcSFTPMonitors type,
203 const SilcSFTPMonitorData data,
206 SilcClientFtpSession session = (SilcClientFtpSession)context;
208 if (type == SILC_SFTP_MONITOR_READ) {
209 /* Call the monitor for application */
210 if (session->monitor)
211 (*session->monitor)(session->client, session->conn,
212 SILC_CLIENT_FILE_MONITOR_SEND,
214 data->offset, session->filesize,
215 session->client_entry, session->session_id,
216 session->filepath, session->monitor_context);
220 /* Returns the read data. This is the downloader's function (client side)
221 to receive the read data and read more until EOF is received from
222 the other side. This will also monitor the transmission and notify
225 static void silc_client_ftp_data(SilcSFTP sftp,
226 SilcSFTPStatus status,
227 const unsigned char *data,
231 SilcClientFtpSession session = (SilcClientFtpSession)context;
233 SILC_LOG_DEBUG(("Start"));
235 if (status == SILC_SFTP_STATUS_EOF) {
238 /* Close the handle */
239 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
240 session->read_handle = NULL;
242 /* Close the read file descriptor */
243 silc_file_close(session->fd);
247 if (status != SILC_SFTP_STATUS_OK) {
248 /* Call monitor callback */
249 if (session->monitor)
250 (*session->monitor)(session->client, session->conn,
251 SILC_CLIENT_FILE_MONITOR_ERROR,
252 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
253 SILC_CLIENT_FILE_NO_SUCH_FILE :
254 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
255 SILC_CLIENT_FILE_PERMISSION_DENIED :
256 SILC_CLIENT_FILE_ERROR), 0, 0,
257 session->client_entry, session->session_id,
258 session->filepath, session->monitor_context);
260 /* Close the handle */
261 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
262 session->read_handle = NULL;
264 /* Close the read file descriptor */
265 silc_file_close(session->fd);
269 /* Read more, until EOF is received */
270 session->read_offset += data_len;
271 silc_sftp_read(sftp, session->read_handle, session->read_offset,
272 SILC_PACKET_MAX_LEN - 1024,
273 silc_client_ftp_data, session);
275 /* Call monitor callback */
276 if (session->monitor)
277 (*session->monitor)(session->client, session->conn,
278 SILC_CLIENT_FILE_MONITOR_RECEIVE,
280 session->read_offset, session->filesize,
281 session->client_entry, session->session_id,
282 session->filepath, session->monitor_context);
284 /* Write the read data to the real file */
285 silc_file_write(session->fd, data, data_len);
288 /* Returns handle for the opened file. This is the downloader's function.
289 This will begin reading the data from the file. */
291 static void silc_client_ftp_open_handle(SilcSFTP sftp,
292 SilcSFTPStatus status,
293 SilcSFTPHandle handle,
296 SilcClientFtpSession session = (SilcClientFtpSession)context;
299 SILC_LOG_DEBUG(("Start"));
301 if (status != SILC_SFTP_STATUS_OK) {
302 /* Call monitor callback */
303 if (session->monitor)
304 (*session->monitor)(session->client, session->conn,
305 SILC_CLIENT_FILE_MONITOR_ERROR,
306 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
307 SILC_CLIENT_FILE_NO_SUCH_FILE :
308 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
309 SILC_CLIENT_FILE_PERMISSION_DENIED :
310 SILC_CLIENT_FILE_ERROR), 0, 0,
311 session->client_entry, session->session_id,
312 session->filepath, session->monitor_context);
316 /* Open the actual local file */
317 memset(path, 0, sizeof(path));
318 snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
319 session->path : "", session->filepath);
320 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
321 if (session->fd < 0) {
322 /* Call monitor callback */
323 session->client->internal->ops->say(session->client, session->conn,
324 SILC_CLIENT_MESSAGE_ERROR,
325 "File `%s' open failed: %s",
329 if (session->monitor)
330 (*session->monitor)(session->client, session->conn,
331 SILC_CLIENT_FILE_MONITOR_ERROR,
332 SILC_CLIENT_FILE_ERROR, 0, 0,
333 session->client_entry, session->session_id,
334 session->filepath, session->monitor_context);
338 session->read_handle = handle;
340 /* Now, start reading the file */
341 silc_sftp_read(sftp, session->read_handle, session->read_offset,
342 SILC_PACKET_MAX_LEN - 1024,
343 silc_client_ftp_data, session);
345 /* Call monitor callback */
346 if (session->monitor)
347 (*session->monitor)(session->client, session->conn,
348 SILC_CLIENT_FILE_MONITOR_RECEIVE,
350 session->read_offset, session->filesize,
351 session->client_entry, session->session_id,
352 session->filepath, session->monitor_context);
355 /* Returns the file name available for download. This is the downloader's
358 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
359 SilcSFTPStatus status,
360 const SilcSFTPName name,
363 SilcClientFtpSession session = (SilcClientFtpSession)context;
364 SilcSFTPAttributesStruct attr;
366 SILC_LOG_DEBUG(("Start"));
368 if (status != SILC_SFTP_STATUS_OK) {
369 /* Call monitor callback */
370 if (session->monitor)
371 (*session->monitor)(session->client, session->conn,
372 SILC_CLIENT_FILE_MONITOR_ERROR,
373 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
374 SILC_CLIENT_FILE_NO_SUCH_FILE :
375 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
376 SILC_CLIENT_FILE_PERMISSION_DENIED :
377 SILC_CLIENT_FILE_ERROR), 0, 0,
378 session->client_entry, session->session_id,
379 session->filepath, session->monitor_context);
383 /* Now open the file */
384 memset(&attr, 0, sizeof(attr));
385 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
386 silc_client_ftp_open_handle, session);
388 /* Save the important attributes like filename and file size */
389 session->filepath = strdup(name->filename[0]);
390 session->filesize = name->attrs[0]->size;
392 /* Close the directory handle */
393 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
394 session->dir_handle = NULL;
397 /* Returns the file handle after giving opendir command. This is the
398 downloader's function. */
400 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
401 SilcSFTPStatus status,
402 SilcSFTPHandle handle,
405 SilcClientFtpSession session = (SilcClientFtpSession)context;
407 SILC_LOG_DEBUG(("Start"));
409 if (status != SILC_SFTP_STATUS_OK) {
410 /* Call monitor callback */
411 if (session->monitor)
412 (*session->monitor)(session->client, session->conn,
413 SILC_CLIENT_FILE_MONITOR_ERROR,
414 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
415 SILC_CLIENT_FILE_NO_SUCH_FILE :
416 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
417 SILC_CLIENT_FILE_PERMISSION_DENIED :
418 SILC_CLIENT_FILE_ERROR), 0, 0,
419 session->client_entry, session->session_id,
420 session->filepath, session->monitor_context);
424 /* Now, read the directory */
425 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
426 session->dir_handle = handle;
429 /* SFTP version callback for SFTP client. This is the downloader's function
430 after initializing the SFTP connection to the remote client. This will
431 find out the filename available for download. */
433 static void silc_client_ftp_version(SilcSFTP sftp,
434 SilcSFTPStatus status,
435 SilcSFTPVersion version,
438 SilcClientFtpSession session = (SilcClientFtpSession)context;
440 SILC_LOG_DEBUG(("Start"));
442 if (status != SILC_SFTP_STATUS_OK) {
443 /* Call monitor callback */
444 if (session->monitor)
445 (*session->monitor)(session->client, session->conn,
446 SILC_CLIENT_FILE_MONITOR_ERROR,
447 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
448 SILC_CLIENT_FILE_NO_SUCH_FILE :
449 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
450 SILC_CLIENT_FILE_PERMISSION_DENIED :
451 SILC_CLIENT_FILE_ERROR), 0, 0,
452 session->client_entry, session->session_id,
453 session->filepath, session->monitor_context);
457 /* The SFTP session is open, now retrieve the info about available file. */
458 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
461 /* This callback is called after the key agreement protocol has been
462 performed. This calls the final completion callback for the application. */
464 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
466 SilcProtocol protocol = (SilcProtocol)context;
467 SilcClientKEInternalContext *ctx =
468 (SilcClientKEInternalContext *)protocol->context;
469 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
470 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
472 SILC_LOG_DEBUG(("Start"));
474 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
475 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
476 /* Call monitor callback */
477 if (session->monitor)
478 (*session->monitor)(session->client, session->conn,
479 SILC_CLIENT_FILE_MONITOR_ERROR,
480 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
481 session->client_entry, session->session_id,
482 session->filepath, session->monitor_context);
484 /* Error occured during protocol */
485 silc_ske_free_key_material(ctx->keymat);
489 /* Set keys into use */
490 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
491 ctx->ske->prop->cipher,
492 ctx->ske->prop->pkcs,
493 ctx->ske->prop->hash,
494 ctx->ske->prop->hmac,
495 ctx->ske->prop->group,
498 if (!session->server) {
499 /* If we are the SFTP client then start the SFTP session and retrieve
500 the info about the file available for download. */
501 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
502 session, silc_client_ftp_version,
505 /* Start SFTP server */
506 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
507 session, session->fs);
509 /* Monitor transmission */
510 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
511 silc_client_ftp_monitor, session);
514 /* Set this as active session */
515 conn->internal->active_session = session;
518 silc_ske_free_key_material(ctx->keymat);
520 silc_ske_free(ctx->ske);
521 silc_free(ctx->dest_id);
522 ctx->sock->protocol = NULL;
523 silc_socket_free(ctx->sock);
525 silc_protocol_free(protocol);
528 /* The downloader's function to start the key agreement protocol with the
529 remote client after we have connected to it. */
531 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
534 SilcClient client = session->client;
535 SilcClientKEInternalContext *proto_ctx;
536 SilcProtocol protocol;
537 SilcClientConnection conn;
540 SILC_LOG_DEBUG(("Start"));
542 /* Call monitor callback */
543 if (session->monitor)
544 (*session->monitor)(session->client, session->conn,
545 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
546 SILC_CLIENT_FILE_OK, 0, 0,
547 session->client_entry, session->session_id,
548 NULL, session->monitor_context);
550 /* Add new connection for this session */
551 conn = silc_client_add_connection(client, NULL, session->hostname,
552 session->port, session);
554 /* Allocate new socket connection object */
555 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
556 conn->sock->hostname = strdup(session->hostname);
557 conn->sock->port = silc_net_get_remote_port(sock);
558 session->sock = silc_socket_dup(conn->sock);
560 /* Allocate internal context for key exchange protocol. This is
561 sent as context for the protocol. */
562 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
563 proto_ctx->client = client;
564 proto_ctx->sock = silc_socket_dup(conn->sock);
565 proto_ctx->rng = client->rng;
566 proto_ctx->responder = FALSE;
567 proto_ctx->context = session;
568 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
569 proto_ctx->verify = silc_client_protocol_ke_verify_key;
571 /* Perform key exchange protocol. */
572 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
573 &protocol, (void *)proto_ctx,
574 silc_client_ftp_key_agreement_final);
575 conn->sock->protocol = protocol;
577 /* Register the connection for network input and output. This sets
578 that scheduler will listen for incoming packets for this connection
579 and sets that outgoing packets may be sent to this connection as well.
580 However, this doesn't set the scheduler for outgoing traffic, it will
581 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
582 later when outgoing data is available. */
583 context = (void *)client;
584 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
586 /* Execute the protocol */
587 silc_protocol_execute(protocol, client->schedule, 0, 0);
590 /* The remote client's (the client who made the file available for download)
591 function for accepting incoming connection. This will also start the
592 key agreement protocol with the other client. */
594 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
596 SilcClientFtpSession session = (SilcClientFtpSession)context;
597 SilcClient client = session->client;
598 SilcClientConnection conn;
599 SilcSocketConnection newsocket;
600 SilcClientKEInternalContext *proto_ctx;
603 SILC_LOG_DEBUG(("Start"));
605 sock = silc_net_accept_connection(session->listener);
607 /* Call monitor callback */
608 if (session->monitor)
609 (*session->monitor)(session->client, session->conn,
610 SILC_CLIENT_FILE_MONITOR_ERROR,
611 SILC_CLIENT_FILE_ERROR, 0, 0,
612 session->client_entry, session->session_id,
613 session->filepath, session->monitor_context);
617 /* Set socket options */
618 silc_net_set_socket_nonblock(sock);
619 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
621 /* Allocate new socket connection object */
622 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
624 /* Perform name and address lookups for the remote host. */
625 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
626 if (!newsocket->hostname && !newsocket->ip) {
627 /* Call monitor callback */
628 if (session->monitor)
629 (*session->monitor)(session->client, session->conn,
630 SILC_CLIENT_FILE_MONITOR_ERROR,
631 SILC_CLIENT_FILE_ERROR, 0, 0,
632 session->client_entry, session->session_id,
633 session->filepath, session->monitor_context);
636 if (!newsocket->hostname)
637 newsocket->hostname = strdup(newsocket->ip);
638 newsocket->port = silc_net_get_remote_port(sock);
640 /* Call monitor callback */
641 if (session->monitor)
642 (*session->monitor)(session->client, session->conn,
643 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
644 SILC_CLIENT_FILE_OK, 0, 0,
645 session->client_entry, session->session_id,
646 NULL, session->monitor_context);
648 /* Add new connection for this session */
649 conn = silc_client_add_connection(client, NULL, newsocket->hostname,
650 newsocket->port, session);
651 conn->sock = newsocket;
652 conn->sock->user_data = conn;
653 session->sock = silc_socket_dup(conn->sock);
655 /* Allocate internal context for key exchange protocol. This is
656 sent as context for the protocol. */
657 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
658 proto_ctx->client = client;
659 proto_ctx->sock = silc_socket_dup(conn->sock);
660 proto_ctx->rng = client->rng;
661 proto_ctx->responder = TRUE;
662 proto_ctx->context = session;
663 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
664 proto_ctx->verify = silc_client_protocol_ke_verify_key;
666 /* Prepare the connection for key exchange protocol. We allocate the
667 protocol but will not start it yet. The connector will be the
668 initiator of the protocol thus we will wait for initiation from
669 there before we start the protocol. */
670 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
671 &newsocket->protocol, proto_ctx,
672 silc_client_ftp_key_agreement_final);
674 /* Register the connection for network input and output. This sets
675 that scheduler will listen for incoming packets for this connection
676 and sets that outgoing packets may be sent to this connection as well.
677 However, this doesn't set the scheduler for outgoing traffic, it
678 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
679 later when outgoing data is available. */
680 context = (void *)client;
681 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
684 /* Free all file transfer sessions. */
686 void silc_client_ftp_free_sessions(SilcClient client,
687 SilcClientConnection conn)
689 if (conn->internal->ftp_sessions) {
690 SilcClientFtpSession session;
691 silc_dlist_start(conn->internal->ftp_sessions);
692 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
695 session->sock->user_data = NULL;
696 silc_client_ftp_session_free(session);
698 silc_dlist_del(conn->internal->ftp_sessions, session);
702 /* Free file transfer session by client entry. */
704 void silc_client_ftp_session_free_client(SilcClientConnection conn,
705 SilcClientEntry client_entry)
707 SilcClientFtpSession session;
709 if (!conn->internal->ftp_sessions)
712 /* Get the session */
713 silc_dlist_start(conn->internal->ftp_sessions);
714 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
716 if (session->client_entry == client_entry) {
718 session->sock->user_data = NULL;
719 silc_client_ftp_session_free(session);
724 /* Free session resources. */
726 void silc_client_ftp_session_free(SilcClientFtpSession session)
728 SilcClientConnection conn;
730 SILC_LOG_DEBUG(("Free session"));
732 if (session->conn && session->conn->internal->ftp_sessions)
733 silc_dlist_del(session->conn->internal->ftp_sessions, session);
735 if (session->conn && session->conn->internal->active_session == session)
736 session->conn->internal->active_session = NULL;
740 silc_sftp_server_shutdown(session->sftp);
742 silc_sftp_client_shutdown(session->sftp);
746 silc_sftp_fs_memory_free(session->fs);
748 /* Destroy listener */
749 if (session->listener) {
750 silc_schedule_unset_listen_fd(session->client->schedule,
752 silc_net_close_connection(session->listener);
753 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
756 /* Destroy session connection */
758 silc_schedule_unset_listen_fd(session->client->schedule,
759 session->sock->sock);
760 silc_net_close_connection(session->sock->sock);
762 if (session->sock->user_data) {
763 conn = (SilcClientConnection)session->sock->user_data;
765 if (conn->internal->active_session == session)
766 conn->internal->active_session = NULL;
768 silc_client_close_connection_real(session->client, session->sock, conn);
770 silc_socket_free(session->sock);
775 silc_buffer_free(session->packet);
777 silc_free(session->hostname);
778 silc_free(session->filepath);
779 silc_free(session->path);
780 memset(session, 'F', sizeof(*session));
784 /* Sends a file indicated by the `filepath' to the remote client
785 indicated by the `client_entry'. This will negotiate a secret key
786 with the remote client before actually starting the transmission of
787 the file. The `monitor' callback will be called to monitor the
788 transmission of the file. */
791 silc_client_file_send(SilcClient client,
792 SilcClientConnection conn,
793 SilcClientFileMonitor monitor,
794 void *monitor_context,
795 const char *local_ip,
796 SilcUInt32 local_port,
798 SilcClientEntry client_entry,
799 const char *filepath,
800 SilcUInt32 *session_id)
802 SilcClientFtpSession session;
803 SilcBuffer keyagr, ftp;
804 char *filename, *path;
807 assert(client && conn && client_entry);
809 SILC_LOG_DEBUG(("Start"));
811 /* Check for existing session for `filepath'. */
812 silc_dlist_start(conn->internal->ftp_sessions);
813 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
815 if (session->filepath && !strcmp(session->filepath, filepath) &&
816 session->client_entry == client_entry)
817 return SILC_CLIENT_FILE_ALREADY_STARTED;
820 /* See whether the file exists, and can be opened in generally speaking */
821 fd = silc_file_open(filepath, O_RDONLY);
823 return SILC_CLIENT_FILE_NO_SUCH_FILE;
826 /* Add new session */
827 session = silc_calloc(1, sizeof(*session));
828 session->session_id = ++conn->internal->next_session_id;
829 session->client = client;
830 session->conn = conn;
831 session->server = TRUE;
832 session->client_entry = client_entry;
833 session->monitor = monitor;
834 session->monitor_context = monitor_context;
835 session->filepath = strdup(filepath);
836 silc_dlist_add(conn->internal->ftp_sessions, session);
838 path = silc_calloc(strlen(filepath) + 9, sizeof(*path));
839 silc_strncat(path, strlen(filepath) + 9, "file://", 7);
840 silc_strncat(path, strlen(filepath) + 9, filepath, strlen(filepath));
842 /* Allocate memory filesystem and put the file to it */
843 if (strrchr(path, '/'))
844 filename = strrchr(path, '/') + 1;
846 filename = (char *)path;
847 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
848 SILC_SFTP_FS_PERM_EXEC);
849 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
852 session->filesize = silc_file_size(filepath);
854 /* Create the listener for incoming key exchange protocol. */
857 session->hostname = strdup(local_ip);
859 session->hostname = silc_net_localip();
860 session->listener = silc_net_create_server(local_port, session->hostname);
861 if (session->listener < 0) {
862 /* Could not create listener. Do the second best thing; send empty
863 key agreement packet and let the remote client provide the point
864 for the key exchange. */
865 SILC_LOG_DEBUG(("Could not create listener"));
866 silc_free(session->hostname);
867 session->hostname = NULL;
871 SILC_LOG_DEBUG(("Bound listener"));
872 session->port = silc_net_get_local_port(session->listener);
873 silc_schedule_task_add(client->schedule, session->listener,
874 silc_client_ftp_process_key_agreement, session,
875 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
876 session->bound = TRUE;
880 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
882 /* Send the key agreement inside FTP packet */
883 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
885 ftp = silc_buffer_alloc(1 + keyagr->len);
886 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
887 silc_buffer_format(ftp,
889 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
891 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
892 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
893 ftp->data, ftp->len, FALSE);
895 silc_buffer_free(keyagr);
896 silc_buffer_free(ftp);
900 *session_id = session->session_id;
902 return SILC_CLIENT_FILE_OK;
905 /* Receives a file from a client indicated by the `client_entry'. The
906 `session_id' indicates the file transmission session and it has been
907 received in the `ftp' client operation function. This will actually
908 perform the key agreement protocol with the remote client before
909 actually starting the file transmission. The `monitor' callback
910 will be called to monitor the transmission. */
913 silc_client_file_receive(SilcClient client,
914 SilcClientConnection conn,
915 SilcClientFileMonitor monitor,
916 void *monitor_context,
918 SilcUInt32 session_id)
920 SilcClientFtpSession session;
921 SilcBuffer keyagr, ftp;
923 assert(client && conn);
925 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
927 /* Get the session */
928 silc_dlist_start(conn->internal->ftp_sessions);
929 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
931 if (session->session_id == session_id) {
936 if (session == SILC_LIST_END) {
937 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
938 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
941 /* See if we have this session running already */
942 if (session->sftp || session->listener) {
943 SILC_LOG_DEBUG(("Session already started"));
944 return SILC_CLIENT_FILE_ALREADY_STARTED;
947 session->monitor = monitor;
948 session->monitor_context = monitor_context;
949 session->conn = conn;
950 session->path = path ? strdup(path) : NULL;
952 /* If the hostname and port already exists then the remote client did
953 provide the connection point to us and we won't create listener, but
954 create the connection ourselves. */
955 if (session->hostname && session->port) {
956 SILC_LOG_DEBUG(("Connecting to remote client"));
957 if (silc_client_connect_to_client(client, conn, session->port,
958 session->hostname, session) < 0)
959 return SILC_CLIENT_FILE_ERROR;
961 /* Add the listener for the key agreement */
962 SILC_LOG_DEBUG(("Creating listener for file transfer"));
963 session->hostname = silc_net_localip();
964 session->listener = silc_net_create_server(0, session->hostname);
965 if (session->listener < 0) {
966 SILC_LOG_DEBUG(("Could not create listener"));
967 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
968 "Cannot create listener on %s: %s",
969 session->hostname, strerror(errno));
970 return SILC_CLIENT_FILE_ERROR;
972 session->port = silc_net_get_local_port(session->listener);
973 silc_schedule_task_add(client->schedule, session->listener,
974 silc_client_ftp_process_key_agreement, session,
975 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
977 /* Send the key agreement inside FTP packet */
978 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
979 keyagr = silc_key_agreement_payload_encode(session->hostname,
981 ftp = silc_buffer_alloc(1 + keyagr->len);
982 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
983 silc_buffer_format(ftp,
985 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
987 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
988 session->client_entry->id,
989 SILC_ID_CLIENT, NULL, NULL,
990 ftp->data, ftp->len, FALSE);
992 silc_buffer_free(keyagr);
993 silc_buffer_free(ftp);
996 return SILC_CLIENT_FILE_OK;
999 /* Closes file transmission session indicated by the `session_id'.
1000 If file transmission is being conducted it will be aborted
1001 automatically. This function is also used to close the session
1002 after successful file transmission. This function can be used
1003 also to reject incoming file transmission request. */
1005 SilcClientFileError silc_client_file_close(SilcClient client,
1006 SilcClientConnection conn,
1007 SilcUInt32 session_id)
1009 SilcClientFtpSession session;
1011 assert(client && conn);
1013 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
1015 /* Get the session */
1016 silc_dlist_start(conn->internal->ftp_sessions);
1017 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1019 if (session->session_id == session_id) {
1024 if (session == SILC_LIST_END) {
1025 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1026 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1029 silc_client_ftp_session_free(session);
1031 return SILC_CLIENT_FILE_OK;
1034 /* Callback called after remote client information has been resolved.
1035 This will try to find existing session for the client entry. If found
1036 then continue with the key agreement protocol. If not then it means
1037 this is a file transfer request and we let the application know. */
1039 static void silc_client_ftp_resolve_cb(SilcClient client,
1040 SilcClientConnection conn,
1041 SilcClientEntry *clients,
1042 SilcUInt32 clients_count,
1045 SilcPacketContext *packet = (SilcPacketContext *)context;
1046 SilcClientFtpSession session;
1047 SilcKeyAgreementPayload payload = NULL;
1048 SilcClientEntry client_entry;
1052 SILC_LOG_DEBUG(("Start"));
1057 client_entry = clients[0];
1059 silc_dlist_start(conn->internal->ftp_sessions);
1060 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1062 if (session->client_entry == client_entry &&
1063 (!session->server || !session->bound))
1067 /* Parse the key agreement payload */
1068 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1069 packet->buffer->len);
1073 hostname = silc_key_agreement_get_hostname(payload);
1074 port = silc_key_agreement_get_port(payload);
1080 if (session == SILC_LIST_END || (!hostname && !port)) {
1081 /* No session found, create one and let the application know about
1082 incoming file transfer request. */
1083 SILC_LOG_DEBUG(("New file transfer session ID: %d",
1084 conn->internal->next_session_id + 1));
1086 /* Add new session */
1087 session = silc_calloc(1, sizeof(*session));
1088 session->session_id = ++conn->internal->next_session_id;
1089 session->client = client;
1090 session->conn = conn;
1091 session->client_entry = client_entry;
1092 silc_dlist_add(conn->internal->ftp_sessions, session);
1094 /* Let the application know */
1095 client->internal->ops->ftp(client, conn, client_entry,
1096 session->session_id, hostname, port);
1098 if (hostname && port) {
1099 session->hostname = strdup(hostname);
1100 session->port = port;
1106 session->hostname = strdup(hostname);
1107 session->port = port;
1109 /* Session exists, continue with key agreement protocol. */
1110 SILC_LOG_DEBUG(("Session ID %d exists, connecting to remote client",
1111 session->session_id));
1112 if (silc_client_connect_to_client(client, conn, port,
1113 hostname, session) < 0) {
1114 /* Call monitor callback */
1115 if (session->monitor)
1116 (*session->monitor)(session->client, session->conn,
1117 SILC_CLIENT_FILE_MONITOR_ERROR,
1118 SILC_CLIENT_FILE_ERROR, 0, 0,
1119 session->client_entry, session->session_id,
1120 session->filepath, session->monitor_context);
1125 silc_key_agreement_payload_free(payload);
1126 silc_packet_context_free(packet);
1129 /* Called when file transfer packet is received. This will parse the
1130 packet and give it to the file transfer protocol. */
1132 void silc_client_ftp(SilcClient client,
1133 SilcSocketConnection sock,
1134 SilcPacketContext *packet)
1136 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1140 SILC_LOG_DEBUG(("Start"));
1142 /* Parse the payload */
1143 ret = silc_buffer_unformat(packet->buffer,
1144 SILC_STR_UI_CHAR(&type),
1149 /* We support only type number 1 (== SFTP) */
1153 silc_buffer_pull(packet->buffer, 1);
1155 /* If we have active FTP session then give the packet directly to the
1156 protocol processor. */
1157 if (conn->internal->active_session) {
1158 /* Give it to the SFTP */
1159 if (conn->internal->active_session->server)
1160 silc_sftp_server_receive_process(conn->internal->active_session->sftp,
1163 silc_sftp_client_receive_process(conn->internal->active_session->sftp,
1166 /* We don't have active session, resolve the remote client information
1167 and then try to find the correct session. */
1168 SilcClientID *remote_id;
1170 if (packet->src_id_type != SILC_ID_CLIENT)
1173 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1178 /* Resolve the client */
1179 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1180 NULL, silc_client_ftp_resolve_cb,
1181 silc_packet_context_dup(packet));
1182 silc_free(remote_id);