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 /* Call monitor callback */
468 if (session->monitor)
469 (*session->monitor)(session->client, session->conn,
470 SILC_CLIENT_FILE_MONITOR_ERROR,
471 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
472 session->client_entry, session->session_id,
473 session->filepath, session->monitor_context);
475 /* Error occured during protocol */
476 silc_ske_free_key_material(ctx->keymat);
480 /* Set keys into use */
481 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
482 ctx->ske->prop->cipher,
483 ctx->ske->prop->pkcs,
484 ctx->ske->prop->hash,
485 ctx->ske->prop->hmac,
486 ctx->ske->prop->group,
489 if (!session->server) {
490 /* If we are the SFTP client then start the SFTP session and retrieve
491 the info about the file available for download. */
492 session->sftp = silc_sftp_client_start(conn->sock,
493 silc_client_ftp_send_packet,
495 silc_client_ftp_version, session);
497 /* Start SFTP server */
498 session->sftp = silc_sftp_server_start(conn->sock,
499 silc_client_ftp_send_packet,
500 session, session->fs);
502 /* Monitor transmission */
503 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
504 silc_client_ftp_monitor, session);
507 /* Set this as active session */
508 conn->active_session = session;
511 silc_ske_free_key_material(ctx->keymat);
513 silc_ske_free(ctx->ske);
514 silc_free(ctx->dest_id);
515 ctx->sock->protocol = NULL;
516 silc_socket_free(ctx->sock);
518 silc_protocol_free(protocol);
521 /* The downloader's function to start the key agreement protocol with the
522 remote client after we have connected to it. */
524 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
527 SilcClient client = session->client;
528 SilcClientKEInternalContext *proto_ctx;
529 SilcProtocol protocol;
530 SilcClientConnection conn;
533 SILC_LOG_DEBUG(("Start"));
535 /* Call monitor callback */
536 if (session->monitor)
537 (*session->monitor)(session->client, session->conn,
538 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
539 SILC_CLIENT_FILE_OK, 0, 0,
540 session->client_entry, session->session_id,
541 NULL, session->monitor_context);
543 /* Add new connection for this session */
544 conn = silc_client_add_connection(client, session->hostname,
545 session->port, session);
547 /* Allocate new socket connection object */
548 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
549 conn->sock->hostname = strdup(session->hostname);
550 conn->sock->port = silc_net_get_remote_port(sock);
551 session->sock = silc_socket_dup(conn->sock);
553 /* Allocate internal context for key exchange protocol. This is
554 sent as context for the protocol. */
555 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
556 proto_ctx->client = client;
557 proto_ctx->sock = silc_socket_dup(conn->sock);
558 proto_ctx->rng = client->rng;
559 proto_ctx->responder = FALSE;
560 proto_ctx->context = session;
561 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
562 proto_ctx->verify = silc_client_protocol_ke_verify_key;
564 /* Perform key exchange protocol. */
565 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
566 &protocol, (void *)proto_ctx,
567 silc_client_ftp_key_agreement_final);
568 conn->sock->protocol = protocol;
570 /* Register the connection for network input and output. This sets
571 that scheduler will listen for incoming packets for this connection
572 and sets that outgoing packets may be sent to this connection as well.
573 However, this doesn't set the scheduler for outgoing traffic, it will
574 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
575 later when outgoing data is available. */
576 context = (void *)client;
577 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
579 /* Execute the protocol */
580 silc_protocol_execute(protocol, client->schedule, 0, 0);
583 /* The remote client's (the client who made the file available for download)
584 function for accepting incoming connection. This will also start the
585 key agreement protocol with the other client. */
587 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
589 SilcClientFtpSession session = (SilcClientFtpSession)context;
590 SilcClient client = session->client;
591 SilcClientConnection conn;
592 SilcSocketConnection newsocket;
593 SilcClientKEInternalContext *proto_ctx;
596 SILC_LOG_DEBUG(("Start"));
598 sock = silc_net_accept_connection(session->listener);
600 /* Call monitor callback */
601 if (session->monitor)
602 (*session->monitor)(session->client, session->conn,
603 SILC_CLIENT_FILE_MONITOR_ERROR,
604 SILC_CLIENT_FILE_ERROR, 0, 0,
605 session->client_entry, session->session_id,
606 session->filepath, session->monitor_context);
610 /* Set socket options */
611 silc_net_set_socket_nonblock(sock);
612 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
614 /* Allocate new socket connection object */
615 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
617 /* Perform name and address lookups for the remote host. */
618 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
619 if (!newsocket->hostname && !newsocket->ip) {
620 /* Call monitor callback */
621 if (session->monitor)
622 (*session->monitor)(session->client, session->conn,
623 SILC_CLIENT_FILE_MONITOR_ERROR,
624 SILC_CLIENT_FILE_ERROR, 0, 0,
625 session->client_entry, session->session_id,
626 session->filepath, session->monitor_context);
629 if (!newsocket->hostname)
630 newsocket->hostname = strdup(newsocket->ip);
631 newsocket->port = silc_net_get_remote_port(sock);
633 /* Call monitor callback */
634 if (session->monitor)
635 (*session->monitor)(session->client, session->conn,
636 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
637 SILC_CLIENT_FILE_OK, 0, 0,
638 session->client_entry, session->session_id,
639 NULL, session->monitor_context);
641 /* Add new connection for this session */
642 conn = silc_client_add_connection(client, newsocket->hostname,
643 newsocket->port, session);
644 conn->sock = newsocket;
645 conn->sock->user_data = conn;
646 session->sock = silc_socket_dup(conn->sock);
648 /* Allocate internal context for key exchange protocol. This is
649 sent as context for the protocol. */
650 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
651 proto_ctx->client = client;
652 proto_ctx->sock = silc_socket_dup(conn->sock);
653 proto_ctx->rng = client->rng;
654 proto_ctx->responder = TRUE;
655 proto_ctx->context = session;
656 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
657 proto_ctx->verify = silc_client_protocol_ke_verify_key;
659 /* Prepare the connection for key exchange protocol. We allocate the
660 protocol but will not start it yet. The connector will be the
661 initiator of the protocol thus we will wait for initiation from
662 there before we start the protocol. */
663 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
664 &newsocket->protocol, proto_ctx,
665 silc_client_ftp_key_agreement_final);
667 /* Register the connection for network input and output. This sets
668 that scheduler will listen for incoming packets for this connection
669 and sets that outgoing packets may be sent to this connection as well.
670 However, this doesn't set the scheduler for outgoing traffic, it
671 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
672 later when outgoing data is available. */
673 context = (void *)client;
674 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
677 /* Free all file transfer sessions. */
679 void silc_client_ftp_free_sessions(SilcClient client,
680 SilcClientConnection conn)
682 if (conn->ftp_sessions) {
683 SilcClientFtpSession session;
684 silc_dlist_start(conn->ftp_sessions);
685 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
687 session->sock->user_data = NULL;
688 silc_client_ftp_session_free(session);
690 silc_dlist_del(conn->ftp_sessions, session);
691 silc_dlist_uninit(conn->ftp_sessions);
695 /* Free file transfer session by client entry. */
697 void silc_client_ftp_session_free_client(SilcClientConnection conn,
698 SilcClientEntry client_entry)
700 SilcClientFtpSession session;
702 if (!conn->ftp_sessions)
705 /* Get the session */
706 silc_dlist_start(conn->ftp_sessions);
707 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
708 if (session->client_entry == client_entry) {
710 session->sock->user_data = NULL;
711 silc_client_ftp_session_free(session);
716 /* Free session resources. */
718 void silc_client_ftp_session_free(SilcClientFtpSession session)
720 SilcClientConnection conn;
722 SILC_LOG_DEBUG(("Free session"));
724 silc_dlist_del(session->conn->ftp_sessions, session);
728 silc_sftp_server_shutdown(session->sftp);
730 silc_sftp_client_shutdown(session->sftp);
734 silc_sftp_fs_memory_free(session->fs);
736 /* Destroy listener */
737 if (session->listener) {
738 silc_schedule_unset_listen_fd(session->client->schedule,
740 silc_net_close_connection(session->listener);
741 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
744 /* Destroy session connection */
746 silc_schedule_unset_listen_fd(session->client->schedule,
747 session->sock->sock);
748 silc_net_close_connection(session->sock->sock);
750 if (session->sock->user_data) {
751 conn = (SilcClientConnection)session->sock->user_data;
753 if (conn->active_session == session)
754 conn->active_session = NULL;
756 silc_client_close_connection(session->client, session->sock, conn);
758 silc_socket_free(session->sock);
763 silc_buffer_free(session->packet);
765 silc_free(session->hostname);
766 silc_free(session->filepath);
770 /* Sends a file indicated by the `filepath' to the remote client
771 indicated by the `client_entry'. This will negotiate a secret key
772 with the remote client before actually starting the transmission of
773 the file. The `monitor' callback will be called to monitor the
774 transmission of the file. */
777 silc_client_file_send(SilcClient client,
778 SilcClientConnection conn,
779 SilcClientFileMonitor monitor,
780 void *monitor_context,
781 const char *local_ip,
783 SilcClientEntry client_entry,
784 const char *filepath,
787 SilcClientFtpSession session;
788 SilcBuffer keyagr, ftp;
789 char *filename, *path;
792 SILC_LOG_DEBUG(("Start"));
794 /* Check for existing session for `filepath'. */
795 silc_dlist_start(conn->ftp_sessions);
796 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
797 if (session->filepath && !strcmp(session->filepath, filepath) &&
798 session->client_entry == client_entry)
799 return SILC_CLIENT_FILE_ALREADY_STARTED;
802 /* See whether the file exists, and can be opened in generally speaking */
803 fd = silc_file_open(filepath, O_RDONLY);
805 return SILC_CLIENT_FILE_NO_SUCH_FILE;
808 /* Add new session */
809 session = silc_calloc(1, sizeof(*session));
810 session->session_id = ++conn->next_session_id;
811 session->client = client;
812 session->conn = conn;
813 session->client_entry = client_entry;
814 session->monitor = monitor;
815 session->monitor_context = monitor_context;
816 session->filepath = strdup(filepath);
817 session->server = TRUE;
818 silc_dlist_add(conn->ftp_sessions, session);
820 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
821 strcat(path, "file://");
822 strncat(path, filepath, strlen(filepath));
824 /* Allocate memory filesystem and put the file to it */
825 if (strrchr(path, '/'))
826 filename = strrchr(path, '/') + 1;
828 filename = (char *)path;
829 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
830 SILC_SFTP_FS_PERM_EXEC);
831 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
834 session->filesize = silc_file_size(filepath);
836 /* Create the listener for incoming key exchange protocol. */
838 session->hostname = strdup(local_ip);
840 session->hostname = silc_net_localip();
841 session->listener = silc_net_create_server(local_port, session->hostname);
842 if (session->listener < 0) {
843 /* Could not create listener. Do the second best thing; send empty
844 key agreement packet and let the remote client provide the point
845 for the key exchange. */
846 SILC_LOG_DEBUG(("Could not create listener"));
847 silc_free(session->hostname);
848 session->hostname = NULL;
852 session->port = silc_net_get_local_port(session->listener);
853 silc_schedule_task_add(client->schedule, session->listener,
854 silc_client_ftp_process_key_agreement, session,
855 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
858 /* Send the key agreement inside FTP packet */
859 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
861 ftp = silc_buffer_alloc(1 + keyagr->len);
862 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
863 silc_buffer_format(ftp,
865 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
867 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
868 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
869 ftp->data, ftp->len, FALSE);
871 silc_buffer_free(keyagr);
872 silc_buffer_free(ftp);
876 *session_id = session->session_id;
878 return SILC_CLIENT_FILE_OK;
881 /* Receives a file from a client indicated by the `client_entry'. The
882 `session_id' indicates the file transmission session and it has been
883 received in the `ftp' client operation function. This will actually
884 perform the key agreement protocol with the remote client before
885 actually starting the file transmission. The `monitor' callback
886 will be called to monitor the transmission. */
889 silc_client_file_receive(SilcClient client,
890 SilcClientConnection conn,
891 SilcClientFileMonitor monitor,
892 void *monitor_context,
895 SilcClientFtpSession session;
896 SilcBuffer keyagr, ftp;
898 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
900 /* Get the session */
901 silc_dlist_start(conn->ftp_sessions);
902 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
903 if (session->session_id == session_id) {
908 if (session == SILC_LIST_END) {
909 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
910 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
913 /* See if we have this session running already */
914 if (session->sftp || session->listener) {
915 SILC_LOG_DEBUG(("Session already started"));
916 return SILC_CLIENT_FILE_ALREADY_STARTED;
919 session->monitor = monitor;
920 session->monitor_context = monitor_context;
921 session->conn = conn;
923 /* If the hostname and port already exists then the remote client did
924 provide the connection point to us and we won't create listener, but
925 create the connection ourselves. */
926 if (session->hostname && session->port) {
927 if (silc_client_connect_to_client(client, conn, session->port,
928 session->hostname, session) < 0)
929 return SILC_CLIENT_FILE_ERROR;
931 /* Add the listener for the key agreement */
932 session->hostname = silc_net_localip();
933 session->listener = silc_net_create_server(0, session->hostname);
934 if (session->listener < 0) {
935 SILC_LOG_DEBUG(("Could not create listener"));
936 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
937 "Cannot create listener on %s: %s",
938 session->hostname, strerror(errno));
939 return SILC_CLIENT_FILE_ERROR;
941 session->port = silc_net_get_local_port(session->listener);
942 silc_schedule_task_add(client->schedule, session->listener,
943 silc_client_ftp_process_key_agreement, session,
944 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
946 /* Send the key agreement inside FTP packet */
947 keyagr = silc_key_agreement_payload_encode(session->hostname,
949 ftp = silc_buffer_alloc(1 + keyagr->len);
950 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
951 silc_buffer_format(ftp,
953 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
955 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
956 session->client_entry->id,
957 SILC_ID_CLIENT, NULL, NULL,
958 ftp->data, ftp->len, FALSE);
960 silc_buffer_free(keyagr);
961 silc_buffer_free(ftp);
964 return SILC_CLIENT_FILE_OK;
967 /* Closes file transmission session indicated by the `session_id'.
968 If file transmission is being conducted it will be aborted
969 automatically. This function is also used to close the session
970 after successful file transmission. This function can be used
971 also to reject incoming file transmission request. */
973 SilcClientFileError silc_client_file_close(SilcClient client,
974 SilcClientConnection conn,
977 SilcClientFtpSession session;
979 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
981 /* Get the session */
982 silc_dlist_start(conn->ftp_sessions);
983 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
984 if (session->session_id == session_id) {
989 if (session == SILC_LIST_END) {
990 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
991 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
994 silc_client_ftp_session_free(session);
996 return SILC_CLIENT_FILE_OK;
999 /* Callback called after remote client information has been resolved.
1000 This will try to find existing session for the client entry. If found
1001 then continue with the key agreement protocol. If not then it means
1002 this is a file transfer request and we let the application know. */
1004 static void silc_client_ftp_resolve_cb(SilcClient client,
1005 SilcClientConnection conn,
1006 SilcClientEntry *clients,
1007 uint32 clients_count,
1010 SilcPacketContext *packet = (SilcPacketContext *)context;
1011 SilcClientFtpSession session;
1012 SilcKeyAgreementPayload payload = NULL;
1013 SilcClientEntry client_entry;
1017 SILC_LOG_DEBUG(("Start"));
1022 client_entry = clients[0];
1024 silc_dlist_start(conn->ftp_sessions);
1025 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1026 if (session->client_entry == client_entry)
1030 /* Parse the key agreement payload */
1031 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1032 packet->buffer->len);
1036 hostname = silc_key_agreement_get_hostname(payload);
1037 port = silc_key_agreement_get_port(payload);
1043 if (session == SILC_LIST_END || (!hostname && !port)) {
1044 /* No session found, create one and let the application know about
1045 incoming file transfer request. */
1047 /* Add new session */
1048 session = silc_calloc(1, sizeof(*session));
1049 session->session_id = ++conn->next_session_id;
1050 session->client = client;
1051 session->conn = conn;
1052 session->client_entry = client_entry;
1053 silc_dlist_add(conn->ftp_sessions, session);
1055 /* Let the application know */
1056 client->internal->ops->ftp(client, conn, client_entry,
1057 session->session_id, hostname, port);
1059 if (hostname && port) {
1060 session->hostname = strdup(hostname);
1061 session->port = port;
1067 session->hostname = strdup(hostname);
1068 session->port = port;
1070 /* Session exists, continue with key agreement protocol. */
1071 if (silc_client_connect_to_client(client, conn, port,
1072 hostname, session) < 0) {
1073 /* Call monitor callback */
1074 if (session->monitor)
1075 (*session->monitor)(session->client, session->conn,
1076 SILC_CLIENT_FILE_MONITOR_ERROR,
1077 SILC_CLIENT_FILE_ERROR, 0, 0,
1078 session->client_entry, session->session_id,
1079 session->filepath, session->monitor_context);
1084 silc_key_agreement_payload_free(payload);
1085 silc_packet_context_free(packet);
1088 /* Called when file transfer packet is received. This will parse the
1089 packet and give it to the file transfer protocol. */
1091 void silc_client_ftp(SilcClient client,
1092 SilcSocketConnection sock,
1093 SilcPacketContext *packet)
1095 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1099 SILC_LOG_DEBUG(("Start"));
1101 /* Parse the payload */
1102 ret = silc_buffer_unformat(packet->buffer,
1103 SILC_STR_UI_CHAR(&type),
1108 /* We support only type number 1 (== SFTP) */
1112 silc_buffer_pull(packet->buffer, 1);
1114 /* If we have active FTP session then give the packet directly to the
1115 protocol processor. */
1116 if (conn->active_session) {
1117 /* Give it to the SFTP */
1118 if (conn->active_session->server)
1119 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1122 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1125 /* We don't have active session, resolve the remote client information
1126 and then try to find the correct session. */
1127 SilcClientID *remote_id;
1129 if (packet->src_id_type != SILC_ID_CLIENT)
1132 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1137 /* Resolve the client */
1138 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1139 silc_client_ftp_resolve_cb,
1140 silc_packet_context_dup(packet));
1141 silc_free(remote_id);