5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2004 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
26 silc_client_connect_to_client(SilcClient client,
27 SilcClientConnection conn, int port,
28 char *host, void *context);
30 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
31 SILC_TASK_CALLBACK(silc_client_ftp_connected);
32 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
35 /* File transmission session */
36 struct SilcClientFtpSessionStruct {
37 SilcUInt32 session_id;
39 SilcClientConnection conn;
40 SilcClientEntry client_entry;
42 SilcSocketConnection sock;
49 SilcClientFileMonitor monitor;
50 void *monitor_context;
51 SilcClientFileAskName ask_name;
52 void *ask_name_context;
57 SilcSFTPFilesystem fs;
58 unsigned int server : 1; /* File sender sets this to TRUE */
59 unsigned int bound : 1;
61 SilcSFTPHandle dir_handle;
62 SilcSFTPHandle read_handle;
64 SilcUInt64 read_offset;
68 SILC_TASK_CALLBACK(silc_client_ftp_connected)
70 SilcClientInternalConnectContext *ctx =
71 (SilcClientInternalConnectContext *)context;
72 SilcClient client = ctx->client;
73 SilcClientConnection conn = ctx->conn;
74 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
75 int opt, opt_len = sizeof(opt);
77 SILC_LOG_DEBUG(("Start"));
79 /* Check the socket status as it might be in error */
80 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
83 /* Connection failed but lets try again */
84 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
85 "Could not connect to client %s: %s",
86 ctx->host, strerror(opt));
87 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
88 "Connecting to port %d of client %s resumed",
89 ctx->port, ctx->host);
91 /* Unregister old connection try */
92 silc_schedule_unset_listen_fd(client->schedule, fd);
93 silc_net_close_connection(fd);
94 silc_schedule_task_del(client->schedule, ctx->task);
97 silc_client_connect_to_client_internal(ctx);
100 /* Connection failed and we won't try anymore */
101 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
102 "Could not connect to client %s: %s",
103 ctx->host, strerror(opt));
104 silc_schedule_unset_listen_fd(client->schedule, fd);
105 silc_net_close_connection(fd);
106 silc_schedule_task_del(client->schedule, ctx->task);
108 silc_client_ftp_session_free(session);
113 silc_schedule_unset_listen_fd(client->schedule, fd);
114 silc_schedule_task_del(client->schedule, ctx->task);
116 /* Start the key agreement */
117 silc_client_ftp_start_key_agreement(session, fd);
121 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
125 /* Create connection to server asynchronously */
126 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
130 /* Register task that will receive the async connect and will
132 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
133 silc_client_ftp_connected,
136 SILC_TASK_PRI_NORMAL);
137 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
144 silc_client_connect_to_client(SilcClient client,
145 SilcClientConnection conn, int port,
146 char *host, void *context)
148 SilcClientInternalConnectContext *ctx;
150 /* Allocate internal context for connection process. This is
151 needed as we are doing async connecting. */
152 ctx = silc_calloc(1, sizeof(*ctx));
153 ctx->client = client;
155 ctx->host = strdup(host);
158 ctx->context = context;
160 /* Do the actual connecting process */
161 return silc_client_connect_to_client_internal(ctx);
164 /* SFTP packet send callback. This will use preallocated buffer to avoid
165 reallocation of outgoing data buffer everytime. */
167 static void silc_client_ftp_send_packet(SilcBuffer packet, void *context)
169 SilcClientFtpSession session = (SilcClientFtpSession)context;
170 SilcClient client = session->client;
172 SILC_LOG_DEBUG(("Start"));
174 /* Allocate outgoing packet */
175 if (!session->packet)
176 session->packet = silc_buffer_alloc(1 + packet->len);
178 /* Enlarge outgoing packet if needed */
179 if (session->packet->truelen < 1 + packet->len)
180 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
183 silc_buffer_pull_tail(session->packet, 1 + packet->len);
184 silc_buffer_format(session->packet,
186 SILC_STR_UI_XNSTRING(packet->data, packet->len),
189 /* Send the packet immediately */
190 silc_client_packet_send(client, session->sock, SILC_PACKET_FTP, NULL,
191 0, NULL, NULL, session->packet->data,
192 session->packet->len, TRUE);
195 session->packet->data = session->packet->tail = session->packet->head;
196 session->packet->len = 0;
199 /* SFTP monitor callback for SFTP server. This reports the application
200 how the transmission is going along. This function is for the client
201 who made the file available for download. */
203 static void silc_client_ftp_monitor(SilcSFTP sftp,
204 SilcSFTPMonitors type,
205 const SilcSFTPMonitorData data,
208 SilcClientFtpSession session = (SilcClientFtpSession)context;
210 if (type == SILC_SFTP_MONITOR_READ) {
211 /* Call the monitor for application */
212 if (session->monitor)
213 (*session->monitor)(session->client, session->conn,
214 SILC_CLIENT_FILE_MONITOR_SEND,
216 data->offset, session->filesize,
217 session->client_entry, session->session_id,
218 session->filepath, session->monitor_context);
222 /* Returns the read data. This is the downloader's function (client side)
223 to receive the read data and read more until EOF is received from
224 the other side. This will also monitor the transmission and notify
227 static void silc_client_ftp_data(SilcSFTP sftp,
228 SilcSFTPStatus status,
229 const unsigned char *data,
233 SilcClientFtpSession session = (SilcClientFtpSession)context;
235 SILC_LOG_DEBUG(("Start"));
237 if (status == SILC_SFTP_STATUS_EOF) {
240 /* Close the handle */
241 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
242 session->read_handle = NULL;
244 /* Close the read file descriptor */
245 silc_file_close(session->fd);
249 if (status != SILC_SFTP_STATUS_OK) {
250 /* Call monitor callback */
251 if (session->monitor)
252 (*session->monitor)(session->client, session->conn,
253 SILC_CLIENT_FILE_MONITOR_ERROR,
254 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
255 SILC_CLIENT_FILE_NO_SUCH_FILE :
256 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
257 SILC_CLIENT_FILE_PERMISSION_DENIED :
258 SILC_CLIENT_FILE_ERROR), 0, 0,
259 session->client_entry, session->session_id,
260 session->filepath, session->monitor_context);
262 /* Close the handle */
263 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
264 session->read_handle = NULL;
266 /* Close the read file descriptor */
267 silc_file_close(session->fd);
271 /* Read more, until EOF is received */
272 session->read_offset += data_len;
273 silc_sftp_read(sftp, session->read_handle, session->read_offset,
274 SILC_PACKET_MAX_LEN - 1024,
275 silc_client_ftp_data, session);
277 /* Call monitor callback */
278 if (session->monitor)
279 (*session->monitor)(session->client, session->conn,
280 SILC_CLIENT_FILE_MONITOR_RECEIVE,
282 session->read_offset, session->filesize,
283 session->client_entry, session->session_id,
284 session->filepath, session->monitor_context);
286 /* Write the read data to the real file */
287 silc_file_write(session->fd, data, data_len);
290 /* Returns handle for the opened file. This is the downloader's function.
291 This will begin reading the data from the file. */
293 static void silc_client_ftp_open_handle(SilcSFTP sftp,
294 SilcSFTPStatus status,
295 SilcSFTPHandle handle,
298 SilcClientFtpSession session = (SilcClientFtpSession)context;
301 SILC_LOG_DEBUG(("Start"));
303 if (status != SILC_SFTP_STATUS_OK) {
304 /* Call monitor callback */
305 if (session->monitor)
306 (*session->monitor)(session->client, session->conn,
307 SILC_CLIENT_FILE_MONITOR_ERROR,
308 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
309 SILC_CLIENT_FILE_NO_SUCH_FILE :
310 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
311 SILC_CLIENT_FILE_PERMISSION_DENIED :
312 SILC_CLIENT_FILE_ERROR), 0, 0,
313 session->client_entry, session->session_id,
314 session->filepath, session->monitor_context);
318 /* Open the actual local file */
319 memset(path, 0, sizeof(path));
320 snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
321 session->path : "", session->filepath);
322 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
323 if (session->fd < 0) {
324 /* Call monitor callback */
325 session->client->internal->ops->say(session->client, session->conn,
326 SILC_CLIENT_MESSAGE_ERROR,
327 "File `%s' open failed: %s",
331 if (session->monitor)
332 (*session->monitor)(session->client, session->conn,
333 SILC_CLIENT_FILE_MONITOR_ERROR,
334 SILC_CLIENT_FILE_PERMISSION_DENIED, 0, 0,
335 session->client_entry, session->session_id,
336 session->filepath, session->monitor_context);
340 session->read_handle = handle;
342 /* Now, start reading the file */
343 silc_sftp_read(sftp, session->read_handle, session->read_offset,
344 SILC_PACKET_MAX_LEN - 1024,
345 silc_client_ftp_data, session);
347 /* Call monitor callback */
348 if (session->monitor)
349 (*session->monitor)(session->client, session->conn,
350 SILC_CLIENT_FILE_MONITOR_RECEIVE,
352 session->read_offset, session->filesize,
353 session->client_entry, session->session_id,
354 session->filepath, session->monitor_context);
357 static void silc_client_ftp_ask_name(const char *filepath,
360 SilcClientFtpSession session = (SilcClientFtpSession)context;
361 SilcSFTPAttributesStruct attr;
362 char *remote_file = NULL;
364 SILC_LOG_DEBUG(("Start"));
367 remote_file = session->filepath;
368 session->filepath = NULL;
369 silc_free(session->path);
370 session->path = NULL;
371 session->filepath = strdup(filepath);
373 remote_file = strdup(session->filepath);
376 /* Now open the file */
377 memset(&attr, 0, sizeof(attr));
378 silc_sftp_open(session->sftp, remote_file, SILC_SFTP_FXF_READ, &attr,
379 silc_client_ftp_open_handle, session);
381 /* Close the directory handle */
382 silc_sftp_close(session->sftp, session->dir_handle, NULL, NULL);
383 session->dir_handle = NULL;
385 silc_free(remote_file);
388 /* Returns the file name available for download. This is the downloader's
391 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
392 SilcSFTPStatus status,
393 const SilcSFTPName name,
396 SilcClientFtpSession session = (SilcClientFtpSession)context;
398 SILC_LOG_DEBUG(("Start"));
400 if (status != SILC_SFTP_STATUS_OK) {
401 /* Call monitor callback */
402 if (session->monitor)
403 (*session->monitor)(session->client, session->conn,
404 SILC_CLIENT_FILE_MONITOR_ERROR,
405 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
406 SILC_CLIENT_FILE_NO_SUCH_FILE :
407 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
408 SILC_CLIENT_FILE_PERMISSION_DENIED :
409 SILC_CLIENT_FILE_ERROR), 0, 0,
410 session->client_entry, session->session_id,
411 session->filepath, session->monitor_context);
415 /* Save the important attributes like filename and file size */
416 session->filepath = strdup(name->filename[0]);
417 session->filesize = name->attrs[0]->size;
419 /* If the path was not provided, ask from application where to save the
421 if (!session->path && session->ask_name) {
422 session->ask_name(session->client, session->conn, session->session_id,
423 name->filename[0], silc_client_ftp_ask_name, session,
424 session->ask_name_context);
428 /* Start downloading immediately to current directory. */
429 silc_client_ftp_ask_name(NULL, session);
432 /* Returns the file handle after giving opendir command. This is the
433 downloader's function. */
435 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
436 SilcSFTPStatus status,
437 SilcSFTPHandle handle,
440 SilcClientFtpSession session = (SilcClientFtpSession)context;
442 SILC_LOG_DEBUG(("Start"));
444 if (status != SILC_SFTP_STATUS_OK) {
445 /* Call monitor callback */
446 if (session->monitor)
447 (*session->monitor)(session->client, session->conn,
448 SILC_CLIENT_FILE_MONITOR_ERROR,
449 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
450 SILC_CLIENT_FILE_NO_SUCH_FILE :
451 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
452 SILC_CLIENT_FILE_PERMISSION_DENIED :
453 SILC_CLIENT_FILE_ERROR), 0, 0,
454 session->client_entry, session->session_id,
455 session->filepath, session->monitor_context);
459 /* Now, read the directory */
460 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
461 session->dir_handle = handle;
464 /* SFTP version callback for SFTP client. This is the downloader's function
465 after initializing the SFTP connection to the remote client. This will
466 find out the filename available for download. */
468 static void silc_client_ftp_version(SilcSFTP sftp,
469 SilcSFTPStatus status,
470 SilcSFTPVersion version,
473 SilcClientFtpSession session = (SilcClientFtpSession)context;
475 SILC_LOG_DEBUG(("Start"));
477 if (status != SILC_SFTP_STATUS_OK) {
478 /* Call monitor callback */
479 if (session->monitor)
480 (*session->monitor)(session->client, session->conn,
481 SILC_CLIENT_FILE_MONITOR_ERROR,
482 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
483 SILC_CLIENT_FILE_NO_SUCH_FILE :
484 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
485 SILC_CLIENT_FILE_PERMISSION_DENIED :
486 SILC_CLIENT_FILE_ERROR), 0, 0,
487 session->client_entry, session->session_id,
488 session->filepath, session->monitor_context);
492 /* The SFTP session is open, now retrieve the info about available file. */
493 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
496 /* This callback is called after the key agreement protocol has been
497 performed. This calls the final completion callback for the application. */
499 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
501 SilcProtocol protocol = (SilcProtocol)context;
502 SilcClientKEInternalContext *ctx =
503 (SilcClientKEInternalContext *)protocol->context;
504 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
505 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
507 SILC_LOG_DEBUG(("Start"));
509 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
510 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
511 /* Call monitor callback */
512 if (session->monitor)
513 (*session->monitor)(session->client, session->conn,
514 SILC_CLIENT_FILE_MONITOR_ERROR,
515 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
516 session->client_entry, session->session_id,
517 session->filepath, session->monitor_context);
519 /* Error occured during protocol */
520 silc_ske_free_key_material(ctx->keymat);
524 /* Set keys into use */
525 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
526 ctx->ske->prop->cipher,
527 ctx->ske->prop->pkcs,
528 ctx->ske->prop->hash,
529 ctx->ske->prop->hmac,
530 ctx->ske->prop->group,
533 if (!session->server) {
534 /* If we are the SFTP client then start the SFTP session and retrieve
535 the info about the file available for download. */
536 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
537 session, silc_client_ftp_version,
540 /* Start SFTP server */
541 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
542 session, session->fs);
544 /* Monitor transmission */
545 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
546 silc_client_ftp_monitor, session);
549 /* Set this as active session */
550 conn->internal->active_session = session;
553 silc_ske_free_key_material(ctx->keymat);
555 silc_ske_free(ctx->ske);
556 silc_free(ctx->dest_id);
557 ctx->sock->protocol = NULL;
558 silc_socket_free(ctx->sock);
560 silc_protocol_free(protocol);
563 /* The downloader's function to start the key agreement protocol with the
564 remote client after we have connected to it. */
566 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
569 SilcClient client = session->client;
570 SilcClientKEInternalContext *proto_ctx;
571 SilcProtocol protocol;
572 SilcClientConnection conn;
575 SILC_LOG_DEBUG(("Start"));
577 /* Call monitor callback */
578 if (session->monitor)
579 (*session->monitor)(session->client, session->conn,
580 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
581 SILC_CLIENT_FILE_OK, 0, 0,
582 session->client_entry, session->session_id,
583 NULL, session->monitor_context);
585 /* Add new connection for this session */
586 conn = silc_client_add_connection(client, NULL, session->hostname,
587 session->port, session);
589 /* Allocate new socket connection object */
590 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
591 conn->sock->hostname = strdup(session->hostname);
592 conn->sock->port = silc_net_get_remote_port(sock);
593 session->sock = silc_socket_dup(conn->sock);
595 /* Allocate internal context for key exchange protocol. This is
596 sent as context for the protocol. */
597 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
598 proto_ctx->client = client;
599 proto_ctx->sock = silc_socket_dup(conn->sock);
600 proto_ctx->rng = client->rng;
601 proto_ctx->responder = FALSE;
602 proto_ctx->context = session;
603 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
604 proto_ctx->verify = silc_client_protocol_ke_verify_key;
606 /* Perform key exchange protocol. */
607 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
608 &protocol, (void *)proto_ctx,
609 silc_client_ftp_key_agreement_final);
610 conn->sock->protocol = protocol;
612 /* Register the connection for network input and output. This sets
613 that scheduler will listen for incoming packets for this connection
614 and sets that outgoing packets may be sent to this connection as well.
615 However, this doesn't set the scheduler for outgoing traffic, it will
616 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
617 later when outgoing data is available. */
618 context = (void *)client;
619 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
621 /* Execute the protocol */
622 silc_protocol_execute(protocol, client->schedule, 0, 0);
625 /* The remote client's (the client who made the file available for download)
626 function for accepting incoming connection. This will also start the
627 key agreement protocol with the other client. */
629 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
631 SilcClientFtpSession session = (SilcClientFtpSession)context;
632 SilcClient client = session->client;
633 SilcClientConnection conn;
634 SilcSocketConnection newsocket;
635 SilcClientKEInternalContext *proto_ctx;
638 SILC_LOG_DEBUG(("Start"));
640 sock = silc_net_accept_connection(session->listener);
642 /* Call monitor callback */
643 if (session->monitor)
644 (*session->monitor)(session->client, session->conn,
645 SILC_CLIENT_FILE_MONITOR_ERROR,
646 SILC_CLIENT_FILE_ERROR, 0, 0,
647 session->client_entry, session->session_id,
648 session->filepath, session->monitor_context);
652 /* Set socket options */
653 silc_net_set_socket_nonblock(sock);
654 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
656 /* Allocate new socket connection object */
657 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
659 /* Perform name and address lookups for the remote host. */
660 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
661 if (!newsocket->hostname && !newsocket->ip) {
662 /* Call monitor callback */
663 if (session->monitor)
664 (*session->monitor)(session->client, session->conn,
665 SILC_CLIENT_FILE_MONITOR_ERROR,
666 SILC_CLIENT_FILE_ERROR, 0, 0,
667 session->client_entry, session->session_id,
668 session->filepath, session->monitor_context);
671 if (!newsocket->hostname)
672 newsocket->hostname = strdup(newsocket->ip);
673 newsocket->port = silc_net_get_remote_port(sock);
675 /* Call monitor callback */
676 if (session->monitor)
677 (*session->monitor)(session->client, session->conn,
678 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
679 SILC_CLIENT_FILE_OK, 0, 0,
680 session->client_entry, session->session_id,
681 NULL, session->monitor_context);
683 /* Add new connection for this session */
684 conn = silc_client_add_connection(client, NULL, newsocket->hostname,
685 newsocket->port, session);
686 conn->sock = newsocket;
687 conn->sock->user_data = conn;
688 session->sock = silc_socket_dup(conn->sock);
690 /* Allocate internal context for key exchange protocol. This is
691 sent as context for the protocol. */
692 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
693 proto_ctx->client = client;
694 proto_ctx->sock = silc_socket_dup(conn->sock);
695 proto_ctx->rng = client->rng;
696 proto_ctx->responder = TRUE;
697 proto_ctx->context = session;
698 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
699 proto_ctx->verify = silc_client_protocol_ke_verify_key;
701 /* Prepare the connection for key exchange protocol. We allocate the
702 protocol but will not start it yet. The connector will be the
703 initiator of the protocol thus we will wait for initiation from
704 there before we start the protocol. */
705 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
706 &newsocket->protocol, proto_ctx,
707 silc_client_ftp_key_agreement_final);
709 /* Register the connection for network input and output. This sets
710 that scheduler will listen for incoming packets for this connection
711 and sets that outgoing packets may be sent to this connection as well.
712 However, this doesn't set the scheduler for outgoing traffic, it
713 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
714 later when outgoing data is available. */
715 context = (void *)client;
716 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
719 /* Free all file transfer sessions. */
721 void silc_client_ftp_free_sessions(SilcClient client,
722 SilcClientConnection conn)
724 if (conn->internal->ftp_sessions) {
725 SilcClientFtpSession session;
726 silc_dlist_start(conn->internal->ftp_sessions);
727 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
730 session->sock->user_data = NULL;
731 silc_client_ftp_session_free(session);
733 silc_dlist_del(conn->internal->ftp_sessions, session);
737 /* Free file transfer session by client entry. */
739 void silc_client_ftp_session_free_client(SilcClientConnection conn,
740 SilcClientEntry client_entry)
742 SilcClientFtpSession session;
744 if (!conn->internal->ftp_sessions)
747 /* Get the session */
748 silc_dlist_start(conn->internal->ftp_sessions);
749 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
751 if (session->client_entry == client_entry)
752 silc_client_ftp_session_free(session);
756 /* Free session resources. */
758 void silc_client_ftp_session_free(SilcClientFtpSession session)
760 SilcClientConnection conn;
762 SILC_LOG_DEBUG(("Free session"));
764 if (session->conn && session->conn->internal->ftp_sessions)
765 silc_dlist_del(session->conn->internal->ftp_sessions, session);
767 if (session->conn && session->conn->internal->active_session == session)
768 session->conn->internal->active_session = NULL;
772 silc_sftp_server_shutdown(session->sftp);
774 silc_sftp_client_shutdown(session->sftp);
778 silc_sftp_fs_memory_free(session->fs);
780 /* Destroy listener */
781 if (session->listener) {
782 silc_schedule_unset_listen_fd(session->client->schedule,
784 silc_net_close_connection(session->listener);
785 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
788 /* Destroy session connection */
790 silc_schedule_unset_listen_fd(session->client->schedule,
791 session->sock->sock);
792 silc_net_close_connection(session->sock->sock);
794 if (session->sock->user_data) {
795 conn = (SilcClientConnection)session->sock->user_data;
797 if (conn->internal->active_session == session)
798 conn->internal->active_session = NULL;
800 silc_client_close_connection_real(session->client, session->sock, conn);
802 silc_socket_free(session->sock);
807 silc_buffer_free(session->packet);
809 silc_free(session->hostname);
810 silc_free(session->filepath);
811 silc_free(session->path);
812 memset(session, 'F', sizeof(*session));
816 /* Sends a file indicated by the `filepath' to the remote client
817 indicated by the `client_entry'. This will negotiate a secret key
818 with the remote client before actually starting the transmission of
819 the file. The `monitor' callback will be called to monitor the
820 transmission of the file. */
823 silc_client_file_send(SilcClient client,
824 SilcClientConnection conn,
825 SilcClientFileMonitor monitor,
826 void *monitor_context,
827 const char *local_ip,
828 SilcUInt32 local_port,
829 SilcBool do_not_bind,
830 SilcClientEntry client_entry,
831 const char *filepath,
832 SilcUInt32 *session_id)
834 SilcClientFtpSession session;
835 SilcBuffer keyagr, ftp;
836 char *filename, *path;
839 assert(client && conn && client_entry);
841 SILC_LOG_DEBUG(("Start"));
843 /* Check for existing session for `filepath'. */
844 silc_dlist_start(conn->internal->ftp_sessions);
845 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
847 if (session->filepath && !strcmp(session->filepath, filepath) &&
848 session->client_entry == client_entry)
849 return SILC_CLIENT_FILE_ALREADY_STARTED;
852 /* See whether the file exists, and can be opened in generally speaking */
853 fd = silc_file_open(filepath, O_RDONLY);
855 return SILC_CLIENT_FILE_NO_SUCH_FILE;
858 /* Add new session */
859 session = silc_calloc(1, sizeof(*session));
860 session->session_id = ++conn->internal->next_session_id;
861 session->client = client;
862 session->conn = conn;
863 session->server = TRUE;
864 session->client_entry = client_entry;
865 session->monitor = monitor;
866 session->monitor_context = monitor_context;
867 session->filepath = strdup(filepath);
868 silc_dlist_add(conn->internal->ftp_sessions, session);
870 path = silc_calloc(strlen(filepath) + 9, sizeof(*path));
871 silc_strncat(path, strlen(filepath) + 9, "file://", 7);
872 silc_strncat(path, strlen(filepath) + 9, filepath, strlen(filepath));
874 /* Allocate memory filesystem and put the file to it */
875 if (strrchr(path, '/'))
876 filename = strrchr(path, '/') + 1;
878 filename = (char *)path;
879 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
880 SILC_SFTP_FS_PERM_EXEC);
881 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
884 session->filesize = silc_file_size(filepath);
886 /* Create the listener for incoming key exchange protocol. */
888 session->listener = -1;
890 session->hostname = strdup(local_ip);
892 silc_net_check_local_by_sock(conn->sock->sock, NULL,
894 if (session->hostname)
895 session->listener = silc_net_create_server(local_port,
897 if (session->listener < 0) {
898 /* Could not create listener. Do the second best thing; send empty
899 key agreement packet and let the remote client provide the point
900 for the key exchange. */
901 SILC_LOG_DEBUG(("Could not create listener"));
902 silc_free(session->hostname);
903 session->listener = 0;
904 session->hostname = NULL;
908 SILC_LOG_DEBUG(("Bound listener"));
909 session->port = silc_net_get_local_port(session->listener);
910 silc_schedule_task_add(client->schedule, session->listener,
911 silc_client_ftp_process_key_agreement, session,
912 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
913 session->bound = TRUE;
917 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
919 /* Send the key agreement inside FTP packet */
920 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
922 ftp = silc_buffer_alloc(1 + keyagr->len);
923 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
924 silc_buffer_format(ftp,
926 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
928 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
929 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
930 ftp->data, ftp->len, FALSE);
932 silc_buffer_free(keyagr);
933 silc_buffer_free(ftp);
937 *session_id = session->session_id;
939 return SILC_CLIENT_FILE_OK;
942 /* Receives a file from a client indicated by the `client_entry'. The
943 `session_id' indicates the file transmission session and it has been
944 received in the `ftp' client operation function. This will actually
945 perform the key agreement protocol with the remote client before
946 actually starting the file transmission. The `monitor' callback
947 will be called to monitor the transmission. */
950 silc_client_file_receive(SilcClient client,
951 SilcClientConnection conn,
952 SilcClientFileMonitor monitor,
953 void *monitor_context,
955 SilcUInt32 session_id,
956 SilcClientFileAskName ask_name,
957 void *ask_name_context)
959 SilcClientFtpSession session;
960 SilcBuffer keyagr, ftp;
962 assert(client && conn);
964 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
966 /* Get the session */
967 silc_dlist_start(conn->internal->ftp_sessions);
968 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
970 if (session->session_id == session_id) {
975 if (session == SILC_LIST_END) {
976 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
977 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
980 /* See if we have this session running already */
981 if (session->sftp || session->listener) {
982 SILC_LOG_DEBUG(("Session already started"));
983 return SILC_CLIENT_FILE_ALREADY_STARTED;
986 session->monitor = monitor;
987 session->monitor_context = monitor_context;
988 session->ask_name = ask_name;
989 session->ask_name_context = ask_name_context;
990 session->conn = conn;
991 session->path = path ? strdup(path) : NULL;
993 /* If the hostname and port already exists then the remote client did
994 provide the connection point to us and we won't create listener, but
995 create the connection ourselves. */
996 if (session->hostname && session->port) {
997 SILC_LOG_DEBUG(("Connecting to remote client"));
998 if (silc_client_connect_to_client(client, conn, session->port,
999 session->hostname, session) < 0)
1000 return SILC_CLIENT_FILE_ERROR;
1002 /* Add the listener for the key agreement */
1003 SILC_LOG_DEBUG(("Creating listener for file transfer"));
1004 session->listener = -1;
1005 silc_net_check_local_by_sock(conn->sock->sock, NULL, &session->hostname);
1006 if (session->hostname)
1007 session->listener = silc_net_create_server(0, session->hostname);
1008 if (session->listener < 0) {
1009 SILC_LOG_DEBUG(("Could not create listener"));
1010 session->listener = 0;
1011 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1012 "Cannot create listener on %s: %s",
1013 session->hostname, strerror(errno));
1014 return SILC_CLIENT_FILE_ERROR;
1016 session->port = silc_net_get_local_port(session->listener);
1017 silc_schedule_task_add(client->schedule, session->listener,
1018 silc_client_ftp_process_key_agreement, session,
1019 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
1021 /* Send the key agreement inside FTP packet */
1022 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
1023 keyagr = silc_key_agreement_payload_encode(session->hostname,
1025 ftp = silc_buffer_alloc(1 + keyagr->len);
1026 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
1027 silc_buffer_format(ftp,
1028 SILC_STR_UI_CHAR(1),
1029 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
1031 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
1032 session->client_entry->id,
1033 SILC_ID_CLIENT, NULL, NULL,
1034 ftp->data, ftp->len, FALSE);
1036 silc_buffer_free(keyagr);
1037 silc_buffer_free(ftp);
1040 return SILC_CLIENT_FILE_OK;
1043 SILC_TASK_CALLBACK(silc_client_file_close_final)
1045 silc_client_ftp_session_free(context);
1048 /* Closes file transmission session indicated by the `session_id'.
1049 If file transmission is being conducted it will be aborted
1050 automatically. This function is also used to close the session
1051 after successful file transmission. This function can be used
1052 also to reject incoming file transmission request. */
1054 SilcClientFileError silc_client_file_close(SilcClient client,
1055 SilcClientConnection conn,
1056 SilcUInt32 session_id)
1058 SilcClientFtpSession session;
1060 assert(client && conn);
1062 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
1064 /* Get the session */
1065 silc_dlist_start(conn->internal->ftp_sessions);
1066 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1068 if (session->session_id == session_id)
1072 if (session == SILC_LIST_END) {
1073 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1074 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1077 if (session->monitor)
1078 (*session->monitor)(session->client, session->conn,
1079 SILC_CLIENT_FILE_MONITOR_CLOSED,
1080 SILC_CLIENT_FILE_OK, 0, 0,
1081 session->client_entry, session->session_id,
1082 session->filepath, session->monitor_context);
1084 /* Destroy via timeout */
1085 silc_schedule_task_add(session->client->schedule, 0,
1086 silc_client_file_close_final, session,
1087 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1089 return SILC_CLIENT_FILE_OK;
1092 /* Callback called after remote client information has been resolved.
1093 This will try to find existing session for the client entry. If found
1094 then continue with the key agreement protocol. If not then it means
1095 this is a file transfer request and we let the application know. */
1097 static void silc_client_ftp_resolve_cb(SilcClient client,
1098 SilcClientConnection conn,
1099 SilcClientEntry *clients,
1100 SilcUInt32 clients_count,
1103 SilcPacketContext *packet = (SilcPacketContext *)context;
1104 SilcClientFtpSession session;
1105 SilcKeyAgreementPayload payload = NULL;
1106 SilcClientEntry client_entry;
1110 SILC_LOG_DEBUG(("Start"));
1115 client_entry = clients[0];
1117 silc_dlist_start(conn->internal->ftp_sessions);
1118 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1120 if (session->client_entry == client_entry &&
1121 (!session->server || !session->bound))
1125 /* Parse the key agreement payload */
1126 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1127 packet->buffer->len);
1131 hostname = silc_key_agreement_get_hostname(payload);
1132 port = silc_key_agreement_get_port(payload);
1138 /* If session doesn't exist, we create one and let applicationi know about
1139 incoming file transfer request. If session exists, but we are responder
1140 it means that the remote sent another request and user hasn't even
1141 accepted the first one yet. We assume this session is new session
1143 if (session == SILC_LIST_END || (!hostname && !port) ||
1144 (session && session->server == FALSE)) {
1145 /* No session found, create one and let the application know about
1146 incoming file transfer request. */
1147 SILC_LOG_DEBUG(("New file transfer session ID: %d",
1148 conn->internal->next_session_id + 1));
1150 /* Add new session */
1151 session = silc_calloc(1, sizeof(*session));
1152 session->session_id = ++conn->internal->next_session_id;
1153 session->client = client;
1154 session->conn = conn;
1155 session->client_entry = client_entry;
1156 silc_dlist_add(conn->internal->ftp_sessions, session);
1158 /* Let the application know */
1159 client->internal->ops->ftp(client, conn, client_entry,
1160 session->session_id, hostname, port);
1162 if (hostname && port) {
1163 session->hostname = strdup(hostname);
1164 session->port = port;
1170 /* Session exists, continue with key agreement protocol. */
1171 SILC_LOG_DEBUG(("Session ID %d exists, connecting to remote client",
1172 session->session_id));
1174 session->hostname = strdup(hostname);
1175 session->port = port;
1177 if (silc_client_connect_to_client(client, conn, port,
1178 hostname, session) < 0) {
1179 /* Call monitor callback */
1180 if (session->monitor)
1181 (*session->monitor)(session->client, session->conn,
1182 SILC_CLIENT_FILE_MONITOR_ERROR,
1183 SILC_CLIENT_FILE_ERROR, 0, 0,
1184 session->client_entry, session->session_id,
1185 session->filepath, session->monitor_context);
1190 silc_key_agreement_payload_free(payload);
1191 silc_packet_context_free(packet);
1194 /* Called when file transfer packet is received. This will parse the
1195 packet and give it to the file transfer protocol. */
1197 void silc_client_ftp(SilcClient client,
1198 SilcSocketConnection sock,
1199 SilcPacketContext *packet)
1201 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1205 SILC_LOG_DEBUG(("Start"));
1207 /* Parse the payload */
1208 ret = silc_buffer_unformat(packet->buffer,
1209 SILC_STR_UI_CHAR(&type),
1214 /* We support only type number 1 (== SFTP) */
1218 silc_buffer_pull(packet->buffer, 1);
1220 /* If we have active FTP session then give the packet directly to the
1221 protocol processor. */
1222 if (conn->internal->active_session) {
1223 /* Give it to the SFTP */
1224 if (conn->internal->active_session->server)
1225 silc_sftp_server_receive_process(conn->internal->active_session->sftp,
1228 silc_sftp_client_receive_process(conn->internal->active_session->sftp,
1231 /* We don't have active session, resolve the remote client information
1232 and then try to find the correct session. */
1233 SilcClientID *remote_id;
1235 if (packet->src_id_type != SILC_ID_CLIENT)
1238 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1243 /* Resolve the client */
1244 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1245 NULL, silc_client_ftp_resolve_cb,
1246 silc_packet_context_dup(packet));
1247 silc_free(remote_id);