5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "clientlibincludes.h"
22 #include "client_internal.h"
25 silc_client_connect_to_client(SilcClient client,
26 SilcClientConnection conn, int port,
27 char *host, void *context);
29 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
30 SILC_TASK_CALLBACK(silc_client_ftp_connected);
31 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
34 /* File transmission session */
35 struct SilcClientFtpSessionStruct {
38 SilcClientConnection conn;
39 SilcClientEntry client_entry;
41 SilcSocketConnection sock;
48 SilcClientFileMonitor monitor;
49 void *monitor_context;
53 SilcSFTPFilesystem fs;
56 SilcSFTPHandle dir_handle;
57 SilcSFTPHandle read_handle;
63 SILC_TASK_CALLBACK(silc_client_ftp_connected)
65 SilcClientInternalConnectContext *ctx =
66 (SilcClientInternalConnectContext *)context;
67 SilcClient client = ctx->client;
68 SilcClientConnection conn = ctx->conn;
69 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
70 int opt, opt_len = sizeof(opt);
72 SILC_LOG_DEBUG(("Start"));
74 /* Check the socket status as it might be in error */
75 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
78 /* Connection failed but lets try again */
79 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
80 "Could not connect to client %s: %s",
81 ctx->host, strerror(opt));
82 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
83 "Connecting to port %d of client %s resumed",
84 ctx->port, ctx->host);
86 /* Unregister old connection try */
87 silc_schedule_unset_listen_fd(client->schedule, fd);
88 silc_net_close_connection(fd);
89 silc_schedule_task_del(client->schedule, ctx->task);
92 silc_client_connect_to_client_internal(ctx);
95 /* Connection failed and we won't try anymore */
96 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
97 "Could not connect to client %s: %s",
98 ctx->host, strerror(opt));
99 silc_schedule_unset_listen_fd(client->schedule, fd);
100 silc_net_close_connection(fd);
101 silc_schedule_task_del(client->schedule, ctx->task);
103 silc_client_ftp_session_free(session);
108 silc_schedule_unset_listen_fd(client->schedule, fd);
109 silc_schedule_task_del(client->schedule, ctx->task);
111 /* Start the key agreement */
112 silc_client_ftp_start_key_agreement(session, fd);
116 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
120 /* Create connection to server asynchronously */
121 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
125 /* Register task that will receive the async connect and will
127 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
128 silc_client_ftp_connected,
131 SILC_TASK_PRI_NORMAL);
132 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
138 silc_client_connect_to_client(SilcClient client,
139 SilcClientConnection conn, int port,
140 char *host, void *context)
142 SilcClientInternalConnectContext *ctx;
144 /* Allocate internal context for connection process. This is
145 needed as we are doing async connecting. */
146 ctx = silc_calloc(1, sizeof(*ctx));
147 ctx->client = client;
149 ctx->host = strdup(host);
152 ctx->context = context;
154 /* Do the actual connecting process */
155 return silc_client_connect_to_client_internal(ctx);
158 /* SFTP packet send callback. This will use preallocated buffer to avoid
159 reallocation of outgoing data buffer everytime. */
161 static void silc_client_ftp_send_packet(SilcSocketConnection sock,
162 SilcBuffer packet, void *context)
164 SilcClientFtpSession session = (SilcClientFtpSession)context;
165 SilcClient client = session->client;
167 SILC_LOG_DEBUG(("Start"));
169 /* Allocate outgoing packet */
170 if (!session->packet)
171 session->packet = silc_buffer_alloc(1 + packet->len);
173 /* Enlarge outgoing packet if needed */
174 if (session->packet->truelen < 1 + packet->len)
175 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
178 silc_buffer_pull_tail(session->packet, 1 + packet->len);
179 silc_buffer_format(session->packet,
181 SILC_STR_UI_XNSTRING(packet->data, packet->len),
184 /* Send the packet immediately */
185 silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
186 session->packet->data, session->packet->len, TRUE);
189 session->packet->data = session->packet->tail = session->packet->head;
190 session->packet->len = 0;
193 /* SFTP monitor callback for SFTP server. This reports the application
194 how the transmission is going along. This function is for the client
195 who made the file available for download. */
197 static void silc_client_ftp_monitor(SilcSFTP sftp,
198 SilcSFTPMonitors type,
199 const SilcSFTPMonitorData data,
202 SilcClientFtpSession session = (SilcClientFtpSession)context;
204 if (type == SILC_SFTP_MONITOR_READ) {
205 /* Call the monitor for application */
206 if (session->monitor)
207 (*session->monitor)(session->client, session->conn,
208 SILC_CLIENT_FILE_MONITOR_SEND,
210 data->offset, session->filesize,
211 session->client_entry, session->session_id,
212 session->filepath, session->monitor_context);
216 /* Returns the read data. This is the downloader's function (client side)
217 to receive the read data and read more until EOF is received from
218 the other side. This will also monitor the transmission and notify
221 static void silc_client_ftp_data(SilcSFTP sftp,
222 SilcSFTPStatus status,
223 const unsigned char *data,
227 SilcClientFtpSession session = (SilcClientFtpSession)context;
229 SILC_LOG_DEBUG(("Start"));
231 if (status == SILC_SFTP_STATUS_EOF) {
234 /* Close the handle */
235 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
236 session->read_handle = NULL;
238 /* Close the read file descriptor */
239 silc_file_close(session->fd);
243 if (status != SILC_SFTP_STATUS_OK) {
244 /* Call monitor callback */
245 if (session->monitor)
246 (*session->monitor)(session->client, session->conn,
247 SILC_CLIENT_FILE_MONITOR_ERROR,
248 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
249 SILC_CLIENT_FILE_NO_SUCH_FILE :
250 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
251 SILC_CLIENT_FILE_PERMISSION_DENIED :
252 SILC_CLIENT_FILE_ERROR), 0, 0,
253 session->client_entry, session->session_id,
254 session->filepath, session->monitor_context);
256 /* Close the handle */
257 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
258 session->read_handle = NULL;
260 /* Close the read file descriptor */
261 silc_file_close(session->fd);
265 /* Read more, until EOF is received */
266 session->read_offset += data_len;
267 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
268 silc_client_ftp_data, session);
270 /* Call monitor callback */
271 if (session->monitor)
272 (*session->monitor)(session->client, session->conn,
273 SILC_CLIENT_FILE_MONITOR_RECEIVE,
275 session->read_offset, session->filesize,
276 session->client_entry, session->session_id,
277 session->filepath, session->monitor_context);
279 /* Write the read data to the real file */
280 silc_file_write(session->fd, data, data_len);
283 /* Returns handle for the opened file. This is the downloader's function.
284 This will begin reading the data from the file. */
286 static void silc_client_ftp_open_handle(SilcSFTP sftp,
287 SilcSFTPStatus status,
288 SilcSFTPHandle handle,
291 SilcClientFtpSession session = (SilcClientFtpSession)context;
293 SILC_LOG_DEBUG(("Start"));
295 if (status != SILC_SFTP_STATUS_OK) {
296 /* Call monitor callback */
297 if (session->monitor)
298 (*session->monitor)(session->client, session->conn,
299 SILC_CLIENT_FILE_MONITOR_ERROR,
300 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
301 SILC_CLIENT_FILE_NO_SUCH_FILE :
302 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
303 SILC_CLIENT_FILE_PERMISSION_DENIED :
304 SILC_CLIENT_FILE_ERROR), 0, 0,
305 session->client_entry, session->session_id,
306 session->filepath, session->monitor_context);
310 /* Open the actual local file */
311 session->fd = silc_file_open(session->filepath,
312 O_RDWR | O_CREAT | O_EXCL);
313 if (session->fd < 0) {
314 /* Call monitor callback */
315 session->client->internal->ops->say(session->client, session->conn,
316 SILC_CLIENT_MESSAGE_ERROR,
317 "File `%s' open failed: %s",
321 if (session->monitor)
322 (*session->monitor)(session->client, session->conn,
323 SILC_CLIENT_FILE_MONITOR_ERROR,
324 SILC_CLIENT_FILE_ERROR, 0, 0,
325 session->client_entry, session->session_id,
326 session->filepath, session->monitor_context);
330 session->read_handle = handle;
332 /* Now, start reading the file */
333 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
334 silc_client_ftp_data, session);
336 /* Call monitor callback */
337 if (session->monitor)
338 (*session->monitor)(session->client, session->conn,
339 SILC_CLIENT_FILE_MONITOR_RECEIVE,
341 session->read_offset, session->filesize,
342 session->client_entry, session->session_id,
343 session->filepath, session->monitor_context);
346 /* Returns the file name available for download. This is the downloader's
349 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
350 SilcSFTPStatus status,
351 const SilcSFTPName name,
354 SilcClientFtpSession session = (SilcClientFtpSession)context;
355 SilcSFTPAttributesStruct attr;
357 SILC_LOG_DEBUG(("Start"));
359 if (status != SILC_SFTP_STATUS_OK) {
360 /* Call monitor callback */
361 if (session->monitor)
362 (*session->monitor)(session->client, session->conn,
363 SILC_CLIENT_FILE_MONITOR_ERROR,
364 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
365 SILC_CLIENT_FILE_NO_SUCH_FILE :
366 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
367 SILC_CLIENT_FILE_PERMISSION_DENIED :
368 SILC_CLIENT_FILE_ERROR), 0, 0,
369 session->client_entry, session->session_id,
370 session->filepath, session->monitor_context);
374 /* Now open the file */
375 memset(&attr, 0, sizeof(attr));
376 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
377 silc_client_ftp_open_handle, session);
379 /* Save the important attributes */
380 session->filepath = strdup(name->filename[0]);
381 session->filesize = name->attrs[0]->size;
383 /* Close the directory handle */
384 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
385 session->dir_handle = NULL;
388 /* Returns the file handle after giving opendir command. This is the
389 downloader's function. */
391 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
392 SilcSFTPStatus status,
393 SilcSFTPHandle handle,
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 /* Now, read the directory */
416 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
417 session->dir_handle = handle;
420 /* SFTP version callback for SFTP client. This is the downloader's function
421 after initializing the SFTP connection to the remote client. This will
422 find out the filename available for download. */
424 static void silc_client_ftp_version(SilcSFTP sftp,
425 SilcSFTPStatus status,
426 SilcSFTPVersion version,
429 SilcClientFtpSession session = (SilcClientFtpSession)context;
431 SILC_LOG_DEBUG(("Start"));
433 if (status != SILC_SFTP_STATUS_OK) {
434 /* Call monitor callback */
435 if (session->monitor)
436 (*session->monitor)(session->client, session->conn,
437 SILC_CLIENT_FILE_MONITOR_ERROR,
438 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
439 SILC_CLIENT_FILE_NO_SUCH_FILE :
440 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
441 SILC_CLIENT_FILE_PERMISSION_DENIED :
442 SILC_CLIENT_FILE_ERROR), 0, 0,
443 session->client_entry, session->session_id,
444 session->filepath, session->monitor_context);
448 /* The SFTP session is open, now retrieve the info about available file. */
449 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
452 /* This callback is called after the key agreement protocol has been
453 performed. This calls the final completion callback for the application. */
455 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
457 SilcProtocol protocol = (SilcProtocol)context;
458 SilcClientKEInternalContext *ctx =
459 (SilcClientKEInternalContext *)protocol->context;
460 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
461 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
463 SILC_LOG_DEBUG(("Start"));
465 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
466 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
467 /* Error occured during protocol */
468 silc_ske_free_key_material(ctx->keymat);
472 /* Set keys into use */
473 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
474 ctx->ske->prop->cipher,
475 ctx->ske->prop->pkcs,
476 ctx->ske->prop->hash,
477 ctx->ske->prop->hmac,
478 ctx->ske->prop->group,
481 if (!session->server) {
482 /* If we are the SFTP client then start the SFTP session and retrieve
483 the info about the file available for download. */
484 session->sftp = silc_sftp_client_start(conn->sock,
485 silc_client_ftp_send_packet,
487 silc_client_ftp_version, session);
489 /* Start SFTP server */
490 session->sftp = silc_sftp_server_start(conn->sock,
491 silc_client_ftp_send_packet,
492 session, session->fs);
494 /* Monitor transmission */
495 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
496 silc_client_ftp_monitor, session);
499 /* Set this as active session */
500 conn->active_session = session;
503 silc_ske_free_key_material(ctx->keymat);
505 silc_ske_free(ctx->ske);
506 silc_free(ctx->dest_id);
507 ctx->sock->protocol = NULL;
508 silc_socket_free(ctx->sock);
510 silc_protocol_free(protocol);
513 /* The downloader's function to start the key agreement protocol with the
514 remote client after we have connected to it. */
516 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
519 SilcClient client = session->client;
520 SilcClientKEInternalContext *proto_ctx;
521 SilcProtocol protocol;
522 SilcClientConnection conn;
525 SILC_LOG_DEBUG(("Start"));
527 /* Call monitor callback */
528 if (session->monitor)
529 (*session->monitor)(session->client, session->conn,
530 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
531 SILC_CLIENT_FILE_OK, 0, 0,
532 session->client_entry, session->session_id,
533 NULL, session->monitor_context);
535 /* Add new connection for this session */
536 conn = silc_client_add_connection(client, session->hostname,
537 session->port, session);
539 /* Allocate new socket connection object */
540 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
541 conn->sock->hostname = strdup(session->hostname);
542 conn->sock->port = silc_net_get_remote_port(sock);
543 session->sock = silc_socket_dup(conn->sock);
545 /* Allocate internal context for key exchange protocol. This is
546 sent as context for the protocol. */
547 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
548 proto_ctx->client = client;
549 proto_ctx->sock = silc_socket_dup(conn->sock);
550 proto_ctx->rng = client->rng;
551 proto_ctx->responder = FALSE;
552 proto_ctx->context = session;
553 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
554 proto_ctx->verify = silc_client_protocol_ke_verify_key;
556 /* Perform key exchange protocol. */
557 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
558 &protocol, (void *)proto_ctx,
559 silc_client_ftp_key_agreement_final);
560 conn->sock->protocol = protocol;
562 /* Register the connection for network input and output. This sets
563 that scheduler will listen for incoming packets for this connection
564 and sets that outgoing packets may be sent to this connection as well.
565 However, this doesn't set the scheduler for outgoing traffic, it will
566 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
567 later when outgoing data is available. */
568 context = (void *)client;
569 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
571 /* Execute the protocol */
572 silc_protocol_execute(protocol, client->schedule, 0, 0);
575 /* The remote client's (the client who made the file available for download)
576 function for accepting incoming connection. This will also start the
577 key agreement protocol with the other client. */
579 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
581 SilcClientFtpSession session = (SilcClientFtpSession)context;
582 SilcClient client = session->client;
583 SilcClientConnection conn;
584 SilcSocketConnection newsocket;
585 SilcClientKEInternalContext *proto_ctx;
588 SILC_LOG_DEBUG(("Start"));
590 sock = silc_net_accept_connection(session->listener);
592 /* Call monitor callback */
593 if (session->monitor)
594 (*session->monitor)(session->client, session->conn,
595 SILC_CLIENT_FILE_MONITOR_ERROR,
596 SILC_CLIENT_FILE_ERROR, 0, 0,
597 session->client_entry, session->session_id,
598 session->filepath, session->monitor_context);
602 /* Set socket options */
603 silc_net_set_socket_nonblock(sock);
604 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
606 /* Allocate new socket connection object */
607 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
609 /* Perform name and address lookups for the remote host. */
610 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
611 if (!newsocket->hostname && !newsocket->ip) {
612 /* Call monitor callback */
613 if (session->monitor)
614 (*session->monitor)(session->client, session->conn,
615 SILC_CLIENT_FILE_MONITOR_ERROR,
616 SILC_CLIENT_FILE_ERROR, 0, 0,
617 session->client_entry, session->session_id,
618 session->filepath, session->monitor_context);
621 if (!newsocket->hostname)
622 newsocket->hostname = strdup(newsocket->ip);
623 newsocket->port = silc_net_get_remote_port(sock);
625 /* Call monitor callback */
626 if (session->monitor)
627 (*session->monitor)(session->client, session->conn,
628 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
629 SILC_CLIENT_FILE_OK, 0, 0,
630 session->client_entry, session->session_id,
631 NULL, session->monitor_context);
633 /* Add new connection for this session */
634 conn = silc_client_add_connection(client, newsocket->hostname,
635 newsocket->port, session);
636 conn->sock = newsocket;
637 conn->sock->user_data = conn;
638 session->sock = silc_socket_dup(conn->sock);
640 /* Allocate internal context for key exchange protocol. This is
641 sent as context for the protocol. */
642 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
643 proto_ctx->client = client;
644 proto_ctx->sock = silc_socket_dup(conn->sock);
645 proto_ctx->rng = client->rng;
646 proto_ctx->responder = TRUE;
647 proto_ctx->context = session;
648 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
649 proto_ctx->verify = silc_client_protocol_ke_verify_key;
651 /* Prepare the connection for key exchange protocol. We allocate the
652 protocol but will not start it yet. The connector will be the
653 initiator of the protocol thus we will wait for initiation from
654 there before we start the protocol. */
655 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
656 &newsocket->protocol, proto_ctx,
657 silc_client_ftp_key_agreement_final);
659 /* Register the connection for network input and output. This sets
660 that scheduler will listen for incoming packets for this connection
661 and sets that outgoing packets may be sent to this connection as well.
662 However, this doesn't set the scheduler for outgoing traffic, it
663 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
664 later when outgoing data is available. */
665 context = (void *)client;
666 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
669 /* Free all file transfer sessions. */
671 void silc_client_ftp_free_sessions(SilcClient client,
672 SilcClientConnection conn)
674 if (conn->ftp_sessions) {
675 SilcClientFtpSession session;
676 silc_dlist_start(conn->ftp_sessions);
677 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
679 session->sock->user_data = NULL;
680 silc_client_ftp_session_free(session);
682 silc_dlist_del(conn->ftp_sessions, session);
683 silc_dlist_uninit(conn->ftp_sessions);
687 /* Free file transfer session by client entry. */
689 void silc_client_ftp_session_free_client(SilcClientConnection conn,
690 SilcClientEntry client_entry)
692 SilcClientFtpSession session;
694 if (!conn->ftp_sessions)
697 /* Get the session */
698 silc_dlist_start(conn->ftp_sessions);
699 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
700 if (session->client_entry == client_entry) {
702 session->sock->user_data = NULL;
703 silc_client_ftp_session_free(session);
708 /* Free session resources. */
710 void silc_client_ftp_session_free(SilcClientFtpSession session)
712 SilcClientConnection conn;
714 SILC_LOG_DEBUG(("Free session"));
716 silc_dlist_del(session->conn->ftp_sessions, session);
720 silc_sftp_server_shutdown(session->sftp);
722 silc_sftp_client_shutdown(session->sftp);
726 silc_sftp_fs_memory_free(session->fs);
728 /* Destroy listener */
729 if (session->listener) {
730 silc_schedule_unset_listen_fd(session->client->schedule,
732 silc_net_close_connection(session->listener);
733 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
736 /* Destroy session connection */
738 silc_schedule_unset_listen_fd(session->client->schedule,
739 session->sock->sock);
740 silc_net_close_connection(session->sock->sock);
742 if (session->sock->user_data) {
743 conn = (SilcClientConnection)session->sock->user_data;
745 if (conn->active_session == session)
746 conn->active_session = NULL;
748 silc_client_close_connection(session->client, session->sock, conn);
750 silc_socket_free(session->sock);
755 silc_buffer_free(session->packet);
757 silc_free(session->hostname);
758 silc_free(session->filepath);
762 /* Sends a file indicated by the `filepath' to the remote client
763 indicated by the `client_entry'. This will negotiate a secret key
764 with the remote client before actually starting the transmission of
765 the file. The `monitor' callback will be called to monitor the
766 transmission of the file.
768 This returns a file session ID for the file transmission. It can
769 be used to close the session (and abort the file transmission) by
770 calling the silc_client_file_close function. The session ID is
771 also returned in the `monitor' callback. This returns 0 if the
772 file indicated by the `filepath' is being transmitted to the remote
773 client indicated by the `client_entry', already. */
775 uint32 silc_client_file_send(SilcClient client,
776 SilcClientConnection conn,
777 SilcClientFileMonitor monitor,
778 void *monitor_context,
779 const char *local_ip,
781 SilcClientEntry client_entry,
782 const char *filepath)
784 SilcClientFtpSession session;
785 SilcBuffer keyagr, ftp;
786 char *filename, *path;
788 SILC_LOG_DEBUG(("Start"));
790 /* Check for existing session for `filepath'. */
791 silc_dlist_start(conn->ftp_sessions);
792 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
793 if (!strcmp(session->filepath, filepath) &&
794 session->client_entry == client_entry)
798 /* Add new session */
799 session = silc_calloc(1, sizeof(*session));
800 session->session_id = ++conn->next_session_id;
801 session->client = client;
802 session->conn = conn;
803 session->client_entry = client_entry;
804 session->monitor = monitor;
805 session->monitor_context = monitor_context;
806 session->filepath = strdup(filepath);
807 session->server = TRUE;
808 silc_dlist_add(conn->ftp_sessions, session);
810 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
811 strcat(path, "file://");
812 strncat(path, filepath, strlen(filepath));
814 /* Allocate memory filesystem and put the file to it */
815 if (strrchr(path, '/'))
816 filename = strrchr(path, '/') + 1;
818 filename = (char *)path;
819 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
820 SILC_SFTP_FS_PERM_EXEC);
821 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
824 session->filesize = silc_file_size(filepath);
826 /* Create the listener for incoming key exchange protocol. */
828 session->hostname = strdup(local_ip);
830 session->hostname = silc_net_localip();
831 session->listener = silc_net_create_server(local_port, session->hostname);
832 if (session->listener < 0) {
833 /* Could not create listener. Do the second best thing; send empty
834 key agreement packet and let the remote client provide the point
835 for the key exchange. */
836 SILC_LOG_DEBUG(("Could not create listener"));
837 silc_free(session->hostname);
838 session->hostname = NULL;
841 session->port = silc_net_get_local_port(session->listener);
842 silc_schedule_task_add(client->schedule, session->listener,
843 silc_client_ftp_process_key_agreement, session,
844 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
847 /* Send the key agreement inside FTP packet */
848 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
850 ftp = silc_buffer_alloc(1 + keyagr->len);
851 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
852 silc_buffer_format(ftp,
854 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
856 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
857 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
858 ftp->data, ftp->len, FALSE);
860 silc_buffer_free(keyagr);
861 silc_buffer_free(ftp);
864 return session->session_id;
867 /* Receives a file from a client indicated by the `client_entry'. The
868 `session_id' indicates the file transmission session and it has been
869 received in the `ftp' client operation function. This will actually
870 perform the key agreement protocol with the remote client before
871 actually starting the file transmission. The `monitor' callback
872 will be called to monitor the transmission. */
875 silc_client_file_receive(SilcClient client,
876 SilcClientConnection conn,
877 SilcClientFileMonitor monitor,
878 void *monitor_context,
881 SilcClientFtpSession session;
882 SilcBuffer keyagr, ftp;
884 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
886 /* Get the session */
887 silc_dlist_start(conn->ftp_sessions);
888 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
889 if (session->session_id == session_id) {
894 if (session == SILC_LIST_END) {
895 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
896 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
899 /* See if we have this session running already */
900 if (session->sftp || session->listener) {
901 SILC_LOG_DEBUG(("Session already started"));
902 return SILC_CLIENT_FILE_ALREADY_STARTED;
905 session->monitor = monitor;
906 session->monitor_context = monitor_context;
907 session->conn = conn;
909 /* If the hostname and port already exists then the remote client did
910 provide the connection point to us and we won't create listener, but
911 create the connection ourselves. */
912 if (session->hostname && session->port) {
913 if (silc_client_connect_to_client(client, conn, session->port,
914 session->hostname, session) < 0)
915 return SILC_CLIENT_FILE_ERROR;
917 /* Add the listener for the key agreement */
918 session->hostname = silc_net_localip();
919 session->listener = silc_net_create_server(0, session->hostname);
920 if (session->listener < 0) {
921 SILC_LOG_DEBUG(("Could not create listener"));
922 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
923 "Cannot create listener on %s: %s",
924 session->hostname, strerror(errno));
925 return SILC_CLIENT_FILE_ERROR;
927 session->port = silc_net_get_local_port(session->listener);
928 silc_schedule_task_add(client->schedule, session->listener,
929 silc_client_ftp_process_key_agreement, session,
930 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
932 /* Send the key agreement inside FTP packet */
933 keyagr = silc_key_agreement_payload_encode(session->hostname,
935 ftp = silc_buffer_alloc(1 + keyagr->len);
936 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
937 silc_buffer_format(ftp,
939 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
941 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
942 session->client_entry->id,
943 SILC_ID_CLIENT, NULL, NULL,
944 ftp->data, ftp->len, FALSE);
946 silc_buffer_free(keyagr);
947 silc_buffer_free(ftp);
950 return SILC_CLIENT_FILE_OK;
953 /* Closes file transmission session indicated by the `session_id'.
954 If file transmission is being conducted it will be aborted
955 automatically. This function is also used to close the session
956 after successful file transmission. This function can be used
957 also to reject incoming file transmission request. */
959 SilcClientFileError silc_client_file_close(SilcClient client,
960 SilcClientConnection conn,
963 SilcClientFtpSession session;
965 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
967 /* Get the session */
968 silc_dlist_start(conn->ftp_sessions);
969 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
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 silc_client_ftp_session_free(session);
982 return SILC_CLIENT_FILE_OK;
985 /* Callback called after remote client information has been resolved.
986 This will try to find existing session for the client entry. If found
987 then continue with the key agreement protocol. If not then it means
988 this is a file transfer request and we let the application know. */
990 static void silc_client_ftp_resolve_cb(SilcClient client,
991 SilcClientConnection conn,
992 SilcClientEntry *clients,
993 uint32 clients_count,
996 SilcPacketContext *packet = (SilcPacketContext *)context;
997 SilcClientFtpSession session;
998 SilcKeyAgreementPayload payload = NULL;
999 SilcClientEntry client_entry;
1003 SILC_LOG_DEBUG(("Start"));
1008 client_entry = clients[0];
1010 silc_dlist_start(conn->ftp_sessions);
1011 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1012 if (session->client_entry == client_entry)
1016 /* Parse the key agreement payload */
1017 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1018 packet->buffer->len);
1022 hostname = silc_key_agreement_get_hostname(payload);
1023 port = silc_key_agreement_get_port(payload);
1025 if (session == SILC_LIST_END || (!hostname && !port)) {
1026 /* No session found, create one and let the application know about
1027 incoming file transfer request. */
1029 /* Add new session */
1030 session = silc_calloc(1, sizeof(*session));
1031 session->session_id = ++conn->next_session_id;
1032 session->client = client;
1033 session->conn = conn;
1034 session->client_entry = client_entry;
1035 silc_dlist_add(conn->ftp_sessions, session);
1037 /* Let the application know */
1038 client->internal->ops->ftp(client, conn, client_entry,
1039 session->session_id, hostname, port);
1041 if (hostname && port) {
1042 session->hostname = strdup(hostname);
1043 session->port = port;
1049 session->hostname = strdup(hostname);
1050 session->port = port;
1052 /* Session exists, continue with key agreement protocol. */
1053 if (silc_client_connect_to_client(client, conn, port,
1054 hostname, session) < 0) {
1055 /* Call monitor callback */
1056 if (session->monitor)
1057 (*session->monitor)(session->client, session->conn,
1058 SILC_CLIENT_FILE_MONITOR_ERROR,
1059 SILC_CLIENT_FILE_ERROR, 0, 0,
1060 session->client_entry, session->session_id,
1061 session->filepath, session->monitor_context);
1066 silc_key_agreement_payload_free(payload);
1067 silc_packet_context_free(packet);
1070 /* Called when file transfer packet is received. This will parse the
1071 packet and give it to the file transfer protocol. */
1073 void silc_client_ftp(SilcClient client,
1074 SilcSocketConnection sock,
1075 SilcPacketContext *packet)
1077 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1081 SILC_LOG_DEBUG(("Start"));
1083 /* Parse the payload */
1084 ret = silc_buffer_unformat(packet->buffer,
1085 SILC_STR_UI_CHAR(&type),
1090 /* We support only type number 1 (== SFTP) */
1094 silc_buffer_pull(packet->buffer, 1);
1096 /* If we have active FTP session then give the packet directly to the
1097 protocol processor. */
1098 if (conn->active_session) {
1099 /* Give it to the SFTP */
1100 if (conn->active_session->server)
1101 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1104 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1107 /* We don't have active session, resolve the remote client information
1108 and then try to find the correct session. */
1109 SilcClientID *remote_id;
1111 if (packet->src_id_type != SILC_ID_CLIENT)
1114 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1119 /* Resolve the client */
1120 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1121 silc_client_ftp_resolve_cb,
1122 silc_packet_context_dup(packet));
1123 silc_free(remote_id);