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;
54 SilcSFTPFilesystem fs;
57 SilcSFTPHandle dir_handle;
58 SilcSFTPHandle read_handle;
60 SilcUInt64 read_offset;
64 SILC_TASK_CALLBACK(silc_client_ftp_connected)
66 SilcClientInternalConnectContext *ctx =
67 (SilcClientInternalConnectContext *)context;
68 SilcClient client = ctx->client;
69 SilcClientConnection conn = ctx->conn;
70 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
71 int opt, opt_len = sizeof(opt);
73 SILC_LOG_DEBUG(("Start"));
75 /* Check the socket status as it might be in error */
76 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
79 /* Connection failed but lets try again */
80 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
81 "Could not connect to client %s: %s",
82 ctx->host, strerror(opt));
83 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
84 "Connecting to port %d of client %s resumed",
85 ctx->port, ctx->host);
87 /* Unregister old connection try */
88 silc_schedule_unset_listen_fd(client->schedule, fd);
89 silc_net_close_connection(fd);
90 silc_schedule_task_del(client->schedule, ctx->task);
93 silc_client_connect_to_client_internal(ctx);
96 /* Connection failed and we won't try anymore */
97 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
98 "Could not connect to client %s: %s",
99 ctx->host, strerror(opt));
100 silc_schedule_unset_listen_fd(client->schedule, fd);
101 silc_net_close_connection(fd);
102 silc_schedule_task_del(client->schedule, ctx->task);
104 silc_client_ftp_session_free(session);
109 silc_schedule_unset_listen_fd(client->schedule, fd);
110 silc_schedule_task_del(client->schedule, ctx->task);
112 /* Start the key agreement */
113 silc_client_ftp_start_key_agreement(session, fd);
117 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
121 /* Create connection to server asynchronously */
122 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
126 /* Register task that will receive the async connect and will
128 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
129 silc_client_ftp_connected,
132 SILC_TASK_PRI_NORMAL);
133 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
139 silc_client_connect_to_client(SilcClient client,
140 SilcClientConnection conn, int port,
141 char *host, void *context)
143 SilcClientInternalConnectContext *ctx;
145 /* Allocate internal context for connection process. This is
146 needed as we are doing async connecting. */
147 ctx = silc_calloc(1, sizeof(*ctx));
148 ctx->client = client;
150 ctx->host = strdup(host);
153 ctx->context = context;
155 /* Do the actual connecting process */
156 return silc_client_connect_to_client_internal(ctx);
159 /* SFTP packet send callback. This will use preallocated buffer to avoid
160 reallocation of outgoing data buffer everytime. */
162 static void silc_client_ftp_send_packet(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, session->sock, SILC_PACKET_FTP, NULL,
186 0, NULL, NULL, session->packet->data,
187 session->packet->len, TRUE);
190 session->packet->data = session->packet->tail = session->packet->head;
191 session->packet->len = 0;
194 /* SFTP monitor callback for SFTP server. This reports the application
195 how the transmission is going along. This function is for the client
196 who made the file available for download. */
198 static void silc_client_ftp_monitor(SilcSFTP sftp,
199 SilcSFTPMonitors type,
200 const SilcSFTPMonitorData data,
203 SilcClientFtpSession session = (SilcClientFtpSession)context;
205 if (type == SILC_SFTP_MONITOR_READ) {
206 /* Call the monitor for application */
207 if (session->monitor)
208 (*session->monitor)(session->client, session->conn,
209 SILC_CLIENT_FILE_MONITOR_SEND,
211 data->offset, session->filesize,
212 session->client_entry, session->session_id,
213 session->filepath, session->monitor_context);
217 /* Returns the read data. This is the downloader's function (client side)
218 to receive the read data and read more until EOF is received from
219 the other side. This will also monitor the transmission and notify
222 static void silc_client_ftp_data(SilcSFTP sftp,
223 SilcSFTPStatus status,
224 const unsigned char *data,
228 SilcClientFtpSession session = (SilcClientFtpSession)context;
230 SILC_LOG_DEBUG(("Start"));
232 if (status == SILC_SFTP_STATUS_EOF) {
235 /* Close the handle */
236 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
237 session->read_handle = NULL;
239 /* Close the read file descriptor */
240 silc_file_close(session->fd);
244 if (status != SILC_SFTP_STATUS_OK) {
245 /* Call monitor callback */
246 if (session->monitor)
247 (*session->monitor)(session->client, session->conn,
248 SILC_CLIENT_FILE_MONITOR_ERROR,
249 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
250 SILC_CLIENT_FILE_NO_SUCH_FILE :
251 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
252 SILC_CLIENT_FILE_PERMISSION_DENIED :
253 SILC_CLIENT_FILE_ERROR), 0, 0,
254 session->client_entry, session->session_id,
255 session->filepath, session->monitor_context);
257 /* Close the handle */
258 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
259 session->read_handle = NULL;
261 /* Close the read file descriptor */
262 silc_file_close(session->fd);
266 /* Read more, until EOF is received */
267 session->read_offset += data_len;
268 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
269 silc_client_ftp_data, session);
271 /* Call monitor callback */
272 if (session->monitor)
273 (*session->monitor)(session->client, session->conn,
274 SILC_CLIENT_FILE_MONITOR_RECEIVE,
276 session->read_offset, session->filesize,
277 session->client_entry, session->session_id,
278 session->filepath, session->monitor_context);
280 /* Write the read data to the real file */
281 silc_file_write(session->fd, data, data_len);
284 /* Returns handle for the opened file. This is the downloader's function.
285 This will begin reading the data from the file. */
287 static void silc_client_ftp_open_handle(SilcSFTP sftp,
288 SilcSFTPStatus status,
289 SilcSFTPHandle handle,
292 SilcClientFtpSession session = (SilcClientFtpSession)context;
294 SILC_LOG_DEBUG(("Start"));
296 if (status != SILC_SFTP_STATUS_OK) {
297 /* Call monitor callback */
298 if (session->monitor)
299 (*session->monitor)(session->client, session->conn,
300 SILC_CLIENT_FILE_MONITOR_ERROR,
301 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
302 SILC_CLIENT_FILE_NO_SUCH_FILE :
303 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
304 SILC_CLIENT_FILE_PERMISSION_DENIED :
305 SILC_CLIENT_FILE_ERROR), 0, 0,
306 session->client_entry, session->session_id,
307 session->filepath, session->monitor_context);
311 /* Open the actual local file */
312 session->fd = silc_file_open(session->filepath,
313 O_RDWR | O_CREAT | O_EXCL);
314 if (session->fd < 0) {
315 /* Call monitor callback */
316 session->client->internal->ops->say(session->client, session->conn,
317 SILC_CLIENT_MESSAGE_ERROR,
318 "File `%s' open failed: %s",
322 if (session->monitor)
323 (*session->monitor)(session->client, session->conn,
324 SILC_CLIENT_FILE_MONITOR_ERROR,
325 SILC_CLIENT_FILE_ERROR, 0, 0,
326 session->client_entry, session->session_id,
327 session->filepath, session->monitor_context);
331 session->read_handle = handle;
333 /* Now, start reading the file */
334 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
335 silc_client_ftp_data, session);
337 /* Call monitor callback */
338 if (session->monitor)
339 (*session->monitor)(session->client, session->conn,
340 SILC_CLIENT_FILE_MONITOR_RECEIVE,
342 session->read_offset, session->filesize,
343 session->client_entry, session->session_id,
344 session->filepath, session->monitor_context);
347 /* Returns the file name available for download. This is the downloader's
350 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
351 SilcSFTPStatus status,
352 const SilcSFTPName name,
355 SilcClientFtpSession session = (SilcClientFtpSession)context;
356 SilcSFTPAttributesStruct attr;
358 SILC_LOG_DEBUG(("Start"));
360 if (status != SILC_SFTP_STATUS_OK) {
361 /* Call monitor callback */
362 if (session->monitor)
363 (*session->monitor)(session->client, session->conn,
364 SILC_CLIENT_FILE_MONITOR_ERROR,
365 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
366 SILC_CLIENT_FILE_NO_SUCH_FILE :
367 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
368 SILC_CLIENT_FILE_PERMISSION_DENIED :
369 SILC_CLIENT_FILE_ERROR), 0, 0,
370 session->client_entry, session->session_id,
371 session->filepath, session->monitor_context);
375 /* Now open the file */
376 memset(&attr, 0, sizeof(attr));
377 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
378 silc_client_ftp_open_handle, session);
380 /* Save the important attributes */
381 session->filepath = strdup(name->filename[0]);
382 session->filesize = name->attrs[0]->size;
384 /* Close the directory handle */
385 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
386 session->dir_handle = NULL;
389 /* Returns the file handle after giving opendir command. This is the
390 downloader's function. */
392 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
393 SilcSFTPStatus status,
394 SilcSFTPHandle handle,
397 SilcClientFtpSession session = (SilcClientFtpSession)context;
399 SILC_LOG_DEBUG(("Start"));
401 if (status != SILC_SFTP_STATUS_OK) {
402 /* Call monitor callback */
403 if (session->monitor)
404 (*session->monitor)(session->client, session->conn,
405 SILC_CLIENT_FILE_MONITOR_ERROR,
406 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
407 SILC_CLIENT_FILE_NO_SUCH_FILE :
408 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
409 SILC_CLIENT_FILE_PERMISSION_DENIED :
410 SILC_CLIENT_FILE_ERROR), 0, 0,
411 session->client_entry, session->session_id,
412 session->filepath, session->monitor_context);
416 /* Now, read the directory */
417 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
418 session->dir_handle = handle;
421 /* SFTP version callback for SFTP client. This is the downloader's function
422 after initializing the SFTP connection to the remote client. This will
423 find out the filename available for download. */
425 static void silc_client_ftp_version(SilcSFTP sftp,
426 SilcSFTPStatus status,
427 SilcSFTPVersion version,
430 SilcClientFtpSession session = (SilcClientFtpSession)context;
432 SILC_LOG_DEBUG(("Start"));
434 if (status != SILC_SFTP_STATUS_OK) {
435 /* Call monitor callback */
436 if (session->monitor)
437 (*session->monitor)(session->client, session->conn,
438 SILC_CLIENT_FILE_MONITOR_ERROR,
439 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
440 SILC_CLIENT_FILE_NO_SUCH_FILE :
441 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
442 SILC_CLIENT_FILE_PERMISSION_DENIED :
443 SILC_CLIENT_FILE_ERROR), 0, 0,
444 session->client_entry, session->session_id,
445 session->filepath, session->monitor_context);
449 /* The SFTP session is open, now retrieve the info about available file. */
450 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
453 /* This callback is called after the key agreement protocol has been
454 performed. This calls the final completion callback for the application. */
456 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
458 SilcProtocol protocol = (SilcProtocol)context;
459 SilcClientKEInternalContext *ctx =
460 (SilcClientKEInternalContext *)protocol->context;
461 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
462 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
464 SILC_LOG_DEBUG(("Start"));
466 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
467 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
468 /* Call monitor callback */
469 if (session->monitor)
470 (*session->monitor)(session->client, session->conn,
471 SILC_CLIENT_FILE_MONITOR_ERROR,
472 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
473 session->client_entry, session->session_id,
474 session->filepath, session->monitor_context);
476 /* Error occured during protocol */
477 silc_ske_free_key_material(ctx->keymat);
481 /* Set keys into use */
482 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
483 ctx->ske->prop->cipher,
484 ctx->ske->prop->pkcs,
485 ctx->ske->prop->hash,
486 ctx->ske->prop->hmac,
487 ctx->ske->prop->group,
490 if (!session->server) {
491 /* If we are the SFTP client then start the SFTP session and retrieve
492 the info about the file available for download. */
493 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
494 session, silc_client_ftp_version,
497 /* Start SFTP server */
498 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
499 session, session->fs);
501 /* Monitor transmission */
502 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
503 silc_client_ftp_monitor, session);
506 /* Set this as active session */
507 conn->active_session = session;
510 silc_ske_free_key_material(ctx->keymat);
512 silc_ske_free(ctx->ske);
513 silc_free(ctx->dest_id);
514 ctx->sock->protocol = NULL;
515 silc_socket_free(ctx->sock);
517 silc_protocol_free(protocol);
520 /* The downloader's function to start the key agreement protocol with the
521 remote client after we have connected to it. */
523 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
526 SilcClient client = session->client;
527 SilcClientKEInternalContext *proto_ctx;
528 SilcProtocol protocol;
529 SilcClientConnection conn;
532 SILC_LOG_DEBUG(("Start"));
534 /* Call monitor callback */
535 if (session->monitor)
536 (*session->monitor)(session->client, session->conn,
537 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
538 SILC_CLIENT_FILE_OK, 0, 0,
539 session->client_entry, session->session_id,
540 NULL, session->monitor_context);
542 /* Add new connection for this session */
543 conn = silc_client_add_connection(client, session->hostname,
544 session->port, session);
546 /* Allocate new socket connection object */
547 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
548 conn->sock->hostname = strdup(session->hostname);
549 conn->sock->port = silc_net_get_remote_port(sock);
550 session->sock = silc_socket_dup(conn->sock);
552 /* Allocate internal context for key exchange protocol. This is
553 sent as context for the protocol. */
554 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
555 proto_ctx->client = client;
556 proto_ctx->sock = silc_socket_dup(conn->sock);
557 proto_ctx->rng = client->rng;
558 proto_ctx->responder = FALSE;
559 proto_ctx->context = session;
560 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
561 proto_ctx->verify = silc_client_protocol_ke_verify_key;
563 /* Perform key exchange protocol. */
564 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
565 &protocol, (void *)proto_ctx,
566 silc_client_ftp_key_agreement_final);
567 conn->sock->protocol = protocol;
569 /* Register the connection for network input and output. This sets
570 that scheduler will listen for incoming packets for this connection
571 and sets that outgoing packets may be sent to this connection as well.
572 However, this doesn't set the scheduler for outgoing traffic, it will
573 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
574 later when outgoing data is available. */
575 context = (void *)client;
576 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
578 /* Execute the protocol */
579 silc_protocol_execute(protocol, client->schedule, 0, 0);
582 /* The remote client's (the client who made the file available for download)
583 function for accepting incoming connection. This will also start the
584 key agreement protocol with the other client. */
586 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
588 SilcClientFtpSession session = (SilcClientFtpSession)context;
589 SilcClient client = session->client;
590 SilcClientConnection conn;
591 SilcSocketConnection newsocket;
592 SilcClientKEInternalContext *proto_ctx;
595 SILC_LOG_DEBUG(("Start"));
597 sock = silc_net_accept_connection(session->listener);
599 /* Call monitor callback */
600 if (session->monitor)
601 (*session->monitor)(session->client, session->conn,
602 SILC_CLIENT_FILE_MONITOR_ERROR,
603 SILC_CLIENT_FILE_ERROR, 0, 0,
604 session->client_entry, session->session_id,
605 session->filepath, session->monitor_context);
609 /* Set socket options */
610 silc_net_set_socket_nonblock(sock);
611 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
613 /* Allocate new socket connection object */
614 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
616 /* Perform name and address lookups for the remote host. */
617 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
618 if (!newsocket->hostname && !newsocket->ip) {
619 /* Call monitor callback */
620 if (session->monitor)
621 (*session->monitor)(session->client, session->conn,
622 SILC_CLIENT_FILE_MONITOR_ERROR,
623 SILC_CLIENT_FILE_ERROR, 0, 0,
624 session->client_entry, session->session_id,
625 session->filepath, session->monitor_context);
628 if (!newsocket->hostname)
629 newsocket->hostname = strdup(newsocket->ip);
630 newsocket->port = silc_net_get_remote_port(sock);
632 /* Call monitor callback */
633 if (session->monitor)
634 (*session->monitor)(session->client, session->conn,
635 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
636 SILC_CLIENT_FILE_OK, 0, 0,
637 session->client_entry, session->session_id,
638 NULL, session->monitor_context);
640 /* Add new connection for this session */
641 conn = silc_client_add_connection(client, newsocket->hostname,
642 newsocket->port, session);
643 conn->sock = newsocket;
644 conn->sock->user_data = conn;
645 session->sock = silc_socket_dup(conn->sock);
647 /* Allocate internal context for key exchange protocol. This is
648 sent as context for the protocol. */
649 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
650 proto_ctx->client = client;
651 proto_ctx->sock = silc_socket_dup(conn->sock);
652 proto_ctx->rng = client->rng;
653 proto_ctx->responder = TRUE;
654 proto_ctx->context = session;
655 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
656 proto_ctx->verify = silc_client_protocol_ke_verify_key;
658 /* Prepare the connection for key exchange protocol. We allocate the
659 protocol but will not start it yet. The connector will be the
660 initiator of the protocol thus we will wait for initiation from
661 there before we start the protocol. */
662 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
663 &newsocket->protocol, proto_ctx,
664 silc_client_ftp_key_agreement_final);
666 /* Register the connection for network input and output. This sets
667 that scheduler will listen for incoming packets for this connection
668 and sets that outgoing packets may be sent to this connection as well.
669 However, this doesn't set the scheduler for outgoing traffic, it
670 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
671 later when outgoing data is available. */
672 context = (void *)client;
673 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
676 /* Free all file transfer sessions. */
678 void silc_client_ftp_free_sessions(SilcClient client,
679 SilcClientConnection conn)
681 if (conn->ftp_sessions) {
682 SilcClientFtpSession session;
683 silc_dlist_start(conn->ftp_sessions);
684 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
686 session->sock->user_data = NULL;
687 silc_client_ftp_session_free(session);
689 silc_dlist_del(conn->ftp_sessions, session);
690 silc_dlist_uninit(conn->ftp_sessions);
694 /* Free file transfer session by client entry. */
696 void silc_client_ftp_session_free_client(SilcClientConnection conn,
697 SilcClientEntry client_entry)
699 SilcClientFtpSession session;
701 if (!conn->ftp_sessions)
704 /* Get the session */
705 silc_dlist_start(conn->ftp_sessions);
706 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
707 if (session->client_entry == client_entry) {
709 session->sock->user_data = NULL;
710 silc_client_ftp_session_free(session);
715 /* Free session resources. */
717 void silc_client_ftp_session_free(SilcClientFtpSession session)
719 SilcClientConnection conn;
721 SILC_LOG_DEBUG(("Free session"));
723 silc_dlist_del(session->conn->ftp_sessions, session);
727 silc_sftp_server_shutdown(session->sftp);
729 silc_sftp_client_shutdown(session->sftp);
733 silc_sftp_fs_memory_free(session->fs);
735 /* Destroy listener */
736 if (session->listener) {
737 silc_schedule_unset_listen_fd(session->client->schedule,
739 silc_net_close_connection(session->listener);
740 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
743 /* Destroy session connection */
745 silc_schedule_unset_listen_fd(session->client->schedule,
746 session->sock->sock);
747 silc_net_close_connection(session->sock->sock);
749 if (session->sock->user_data) {
750 conn = (SilcClientConnection)session->sock->user_data;
752 if (conn->active_session == session)
753 conn->active_session = NULL;
755 silc_client_close_connection(session->client, session->sock, conn);
757 silc_socket_free(session->sock);
762 silc_buffer_free(session->packet);
764 silc_free(session->hostname);
765 silc_free(session->filepath);
769 /* Sends a file indicated by the `filepath' to the remote client
770 indicated by the `client_entry'. This will negotiate a secret key
771 with the remote client before actually starting the transmission of
772 the file. The `monitor' callback will be called to monitor the
773 transmission of the file. */
776 silc_client_file_send(SilcClient client,
777 SilcClientConnection conn,
778 SilcClientFileMonitor monitor,
779 void *monitor_context,
780 const char *local_ip,
781 SilcUInt32 local_port,
782 SilcClientEntry client_entry,
783 const char *filepath,
784 SilcUInt32 *session_id)
786 SilcClientFtpSession session;
787 SilcBuffer keyagr, ftp;
788 char *filename, *path;
791 SILC_LOG_DEBUG(("Start"));
793 /* Check for existing session for `filepath'. */
794 silc_dlist_start(conn->ftp_sessions);
795 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
796 if (session->filepath && !strcmp(session->filepath, filepath) &&
797 session->client_entry == client_entry)
798 return SILC_CLIENT_FILE_ALREADY_STARTED;
801 /* See whether the file exists, and can be opened in generally speaking */
802 fd = silc_file_open(filepath, O_RDONLY);
804 return SILC_CLIENT_FILE_NO_SUCH_FILE;
807 /* Add new session */
808 session = silc_calloc(1, sizeof(*session));
809 session->session_id = ++conn->next_session_id;
810 session->client = client;
811 session->conn = conn;
812 session->client_entry = client_entry;
813 session->monitor = monitor;
814 session->monitor_context = monitor_context;
815 session->filepath = strdup(filepath);
816 session->server = TRUE;
817 silc_dlist_add(conn->ftp_sessions, session);
819 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
820 strcat(path, "file://");
821 strncat(path, filepath, strlen(filepath));
823 /* Allocate memory filesystem and put the file to it */
824 if (strrchr(path, '/'))
825 filename = strrchr(path, '/') + 1;
827 filename = (char *)path;
828 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
829 SILC_SFTP_FS_PERM_EXEC);
830 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
833 session->filesize = silc_file_size(filepath);
835 /* Create the listener for incoming key exchange protocol. */
837 session->hostname = strdup(local_ip);
839 session->hostname = silc_net_localip();
840 session->listener = silc_net_create_server(local_port, session->hostname);
841 if (session->listener < 0) {
842 /* Could not create listener. Do the second best thing; send empty
843 key agreement packet and let the remote client provide the point
844 for the key exchange. */
845 SILC_LOG_DEBUG(("Could not create listener"));
846 silc_free(session->hostname);
847 session->hostname = NULL;
851 session->port = silc_net_get_local_port(session->listener);
852 silc_schedule_task_add(client->schedule, session->listener,
853 silc_client_ftp_process_key_agreement, session,
854 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
857 /* Send the key agreement inside FTP packet */
858 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
860 ftp = silc_buffer_alloc(1 + keyagr->len);
861 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
862 silc_buffer_format(ftp,
864 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
866 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
867 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
868 ftp->data, ftp->len, FALSE);
870 silc_buffer_free(keyagr);
871 silc_buffer_free(ftp);
875 *session_id = session->session_id;
877 return SILC_CLIENT_FILE_OK;
880 /* Receives a file from a client indicated by the `client_entry'. The
881 `session_id' indicates the file transmission session and it has been
882 received in the `ftp' client operation function. This will actually
883 perform the key agreement protocol with the remote client before
884 actually starting the file transmission. The `monitor' callback
885 will be called to monitor the transmission. */
888 silc_client_file_receive(SilcClient client,
889 SilcClientConnection conn,
890 SilcClientFileMonitor monitor,
891 void *monitor_context,
892 SilcUInt32 session_id)
894 SilcClientFtpSession session;
895 SilcBuffer keyagr, ftp;
897 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
899 /* Get the session */
900 silc_dlist_start(conn->ftp_sessions);
901 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
902 if (session->session_id == session_id) {
907 if (session == SILC_LIST_END) {
908 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
909 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
912 /* See if we have this session running already */
913 if (session->sftp || session->listener) {
914 SILC_LOG_DEBUG(("Session already started"));
915 return SILC_CLIENT_FILE_ALREADY_STARTED;
918 session->monitor = monitor;
919 session->monitor_context = monitor_context;
920 session->conn = conn;
922 /* If the hostname and port already exists then the remote client did
923 provide the connection point to us and we won't create listener, but
924 create the connection ourselves. */
925 if (session->hostname && session->port) {
926 if (silc_client_connect_to_client(client, conn, session->port,
927 session->hostname, session) < 0)
928 return SILC_CLIENT_FILE_ERROR;
930 /* Add the listener for the key agreement */
931 session->hostname = silc_net_localip();
932 session->listener = silc_net_create_server(0, session->hostname);
933 if (session->listener < 0) {
934 SILC_LOG_DEBUG(("Could not create listener"));
935 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
936 "Cannot create listener on %s: %s",
937 session->hostname, strerror(errno));
938 return SILC_CLIENT_FILE_ERROR;
940 session->port = silc_net_get_local_port(session->listener);
941 silc_schedule_task_add(client->schedule, session->listener,
942 silc_client_ftp_process_key_agreement, session,
943 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
945 /* Send the key agreement inside FTP packet */
946 keyagr = silc_key_agreement_payload_encode(session->hostname,
948 ftp = silc_buffer_alloc(1 + keyagr->len);
949 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
950 silc_buffer_format(ftp,
952 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
954 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
955 session->client_entry->id,
956 SILC_ID_CLIENT, NULL, NULL,
957 ftp->data, ftp->len, FALSE);
959 silc_buffer_free(keyagr);
960 silc_buffer_free(ftp);
963 return SILC_CLIENT_FILE_OK;
966 /* Closes file transmission session indicated by the `session_id'.
967 If file transmission is being conducted it will be aborted
968 automatically. This function is also used to close the session
969 after successful file transmission. This function can be used
970 also to reject incoming file transmission request. */
972 SilcClientFileError silc_client_file_close(SilcClient client,
973 SilcClientConnection conn,
974 SilcUInt32 session_id)
976 SilcClientFtpSession session;
978 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
980 /* Get the session */
981 silc_dlist_start(conn->ftp_sessions);
982 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
983 if (session->session_id == session_id) {
988 if (session == SILC_LIST_END) {
989 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
990 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
993 silc_client_ftp_session_free(session);
995 return SILC_CLIENT_FILE_OK;
998 /* Callback called after remote client information has been resolved.
999 This will try to find existing session for the client entry. If found
1000 then continue with the key agreement protocol. If not then it means
1001 this is a file transfer request and we let the application know. */
1003 static void silc_client_ftp_resolve_cb(SilcClient client,
1004 SilcClientConnection conn,
1005 SilcClientEntry *clients,
1006 SilcUInt32 clients_count,
1009 SilcPacketContext *packet = (SilcPacketContext *)context;
1010 SilcClientFtpSession session;
1011 SilcKeyAgreementPayload payload = NULL;
1012 SilcClientEntry client_entry;
1016 SILC_LOG_DEBUG(("Start"));
1021 client_entry = clients[0];
1023 silc_dlist_start(conn->ftp_sessions);
1024 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1025 if (session->client_entry == client_entry)
1029 /* Parse the key agreement payload */
1030 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1031 packet->buffer->len);
1035 hostname = silc_key_agreement_get_hostname(payload);
1036 port = silc_key_agreement_get_port(payload);
1042 if (session == SILC_LIST_END || (!hostname && !port)) {
1043 /* No session found, create one and let the application know about
1044 incoming file transfer request. */
1046 /* Add new session */
1047 session = silc_calloc(1, sizeof(*session));
1048 session->session_id = ++conn->next_session_id;
1049 session->client = client;
1050 session->conn = conn;
1051 session->client_entry = client_entry;
1052 silc_dlist_add(conn->ftp_sessions, session);
1054 /* Let the application know */
1055 client->internal->ops->ftp(client, conn, client_entry,
1056 session->session_id, hostname, port);
1058 if (hostname && port) {
1059 session->hostname = strdup(hostname);
1060 session->port = port;
1066 session->hostname = strdup(hostname);
1067 session->port = port;
1069 /* Session exists, continue with key agreement protocol. */
1070 if (silc_client_connect_to_client(client, conn, port,
1071 hostname, session) < 0) {
1072 /* Call monitor callback */
1073 if (session->monitor)
1074 (*session->monitor)(session->client, session->conn,
1075 SILC_CLIENT_FILE_MONITOR_ERROR,
1076 SILC_CLIENT_FILE_ERROR, 0, 0,
1077 session->client_entry, session->session_id,
1078 session->filepath, session->monitor_context);
1083 silc_key_agreement_payload_free(payload);
1084 silc_packet_context_free(packet);
1087 /* Called when file transfer packet is received. This will parse the
1088 packet and give it to the file transfer protocol. */
1090 void silc_client_ftp(SilcClient client,
1091 SilcSocketConnection sock,
1092 SilcPacketContext *packet)
1094 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1098 SILC_LOG_DEBUG(("Start"));
1100 /* Parse the payload */
1101 ret = silc_buffer_unformat(packet->buffer,
1102 SILC_STR_UI_CHAR(&type),
1107 /* We support only type number 1 (== SFTP) */
1111 silc_buffer_pull(packet->buffer, 1);
1113 /* If we have active FTP session then give the packet directly to the
1114 protocol processor. */
1115 if (conn->active_session) {
1116 /* Give it to the SFTP */
1117 if (conn->active_session->server)
1118 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1121 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1124 /* We don't have active session, resolve the remote client information
1125 and then try to find the correct session. */
1126 SilcClientID *remote_id;
1128 if (packet->src_id_type != SILC_ID_CLIENT)
1131 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1136 /* Resolve the client */
1137 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1138 silc_client_ftp_resolve_cb,
1139 silc_packet_context_dup(packet));
1140 silc_free(remote_id);