5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
26 silc_client_connect_to_client(SilcClient client,
27 SilcClientConnection conn, int port,
28 char *host, void *context);
30 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
31 SILC_TASK_CALLBACK(silc_client_ftp_connected);
32 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
35 /* File transmission session */
36 struct SilcClientFtpSessionStruct {
37 SilcUInt32 session_id;
39 SilcClientConnection conn;
40 SilcClientEntry client_entry;
42 SilcSocketConnection sock;
49 SilcClientFileMonitor monitor;
50 void *monitor_context;
55 SilcSFTPFilesystem fs;
58 SilcSFTPHandle dir_handle;
59 SilcSFTPHandle read_handle;
61 SilcUInt64 read_offset;
65 SILC_TASK_CALLBACK(silc_client_ftp_connected)
67 SilcClientInternalConnectContext *ctx =
68 (SilcClientInternalConnectContext *)context;
69 SilcClient client = ctx->client;
70 SilcClientConnection conn = ctx->conn;
71 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
72 int opt, opt_len = sizeof(opt);
74 SILC_LOG_DEBUG(("Start"));
76 /* Check the socket status as it might be in error */
77 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
80 /* Connection failed but lets try again */
81 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
82 "Could not connect to client %s: %s",
83 ctx->host, strerror(opt));
84 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
85 "Connecting to port %d of client %s resumed",
86 ctx->port, ctx->host);
88 /* Unregister old connection try */
89 silc_schedule_unset_listen_fd(client->schedule, fd);
90 silc_net_close_connection(fd);
91 silc_schedule_task_del(client->schedule, ctx->task);
94 silc_client_connect_to_client_internal(ctx);
97 /* Connection failed and we won't try anymore */
98 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
99 "Could not connect to client %s: %s",
100 ctx->host, strerror(opt));
101 silc_schedule_unset_listen_fd(client->schedule, fd);
102 silc_net_close_connection(fd);
103 silc_schedule_task_del(client->schedule, ctx->task);
105 silc_client_ftp_session_free(session);
110 silc_schedule_unset_listen_fd(client->schedule, fd);
111 silc_schedule_task_del(client->schedule, ctx->task);
113 /* Start the key agreement */
114 silc_client_ftp_start_key_agreement(session, fd);
118 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
122 /* Create connection to server asynchronously */
123 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
127 /* Register task that will receive the async connect and will
129 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
130 silc_client_ftp_connected,
133 SILC_TASK_PRI_NORMAL);
134 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
141 silc_client_connect_to_client(SilcClient client,
142 SilcClientConnection conn, int port,
143 char *host, void *context)
145 SilcClientInternalConnectContext *ctx;
147 /* Allocate internal context for connection process. This is
148 needed as we are doing async connecting. */
149 ctx = silc_calloc(1, sizeof(*ctx));
150 ctx->client = client;
152 ctx->host = strdup(host);
155 ctx->context = context;
157 /* Do the actual connecting process */
158 return silc_client_connect_to_client_internal(ctx);
161 /* SFTP packet send callback. This will use preallocated buffer to avoid
162 reallocation of outgoing data buffer everytime. */
164 static void silc_client_ftp_send_packet(SilcBuffer packet, void *context)
166 SilcClientFtpSession session = (SilcClientFtpSession)context;
167 SilcClient client = session->client;
169 SILC_LOG_DEBUG(("Start"));
171 /* Allocate outgoing packet */
172 if (!session->packet)
173 session->packet = silc_buffer_alloc(1 + packet->len);
175 /* Enlarge outgoing packet if needed */
176 if (session->packet->truelen < 1 + packet->len)
177 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
180 silc_buffer_pull_tail(session->packet, 1 + packet->len);
181 silc_buffer_format(session->packet,
183 SILC_STR_UI_XNSTRING(packet->data, packet->len),
186 /* Send the packet immediately */
187 silc_client_packet_send(client, session->sock, SILC_PACKET_FTP, NULL,
188 0, NULL, NULL, session->packet->data,
189 session->packet->len, TRUE);
192 session->packet->data = session->packet->tail = session->packet->head;
193 session->packet->len = 0;
196 /* SFTP monitor callback for SFTP server. This reports the application
197 how the transmission is going along. This function is for the client
198 who made the file available for download. */
200 static void silc_client_ftp_monitor(SilcSFTP sftp,
201 SilcSFTPMonitors type,
202 const SilcSFTPMonitorData data,
205 SilcClientFtpSession session = (SilcClientFtpSession)context;
207 if (type == SILC_SFTP_MONITOR_READ) {
208 /* Call the monitor for application */
209 if (session->monitor)
210 (*session->monitor)(session->client, session->conn,
211 SILC_CLIENT_FILE_MONITOR_SEND,
213 data->offset, session->filesize,
214 session->client_entry, session->session_id,
215 session->filepath, session->monitor_context);
219 /* Returns the read data. This is the downloader's function (client side)
220 to receive the read data and read more until EOF is received from
221 the other side. This will also monitor the transmission and notify
224 static void silc_client_ftp_data(SilcSFTP sftp,
225 SilcSFTPStatus status,
226 const unsigned char *data,
230 SilcClientFtpSession session = (SilcClientFtpSession)context;
232 SILC_LOG_DEBUG(("Start"));
234 if (status == SILC_SFTP_STATUS_EOF) {
237 /* Close the handle */
238 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
239 session->read_handle = NULL;
241 /* Close the read file descriptor */
242 silc_file_close(session->fd);
246 if (status != SILC_SFTP_STATUS_OK) {
247 /* Call monitor callback */
248 if (session->monitor)
249 (*session->monitor)(session->client, session->conn,
250 SILC_CLIENT_FILE_MONITOR_ERROR,
251 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
252 SILC_CLIENT_FILE_NO_SUCH_FILE :
253 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
254 SILC_CLIENT_FILE_PERMISSION_DENIED :
255 SILC_CLIENT_FILE_ERROR), 0, 0,
256 session->client_entry, session->session_id,
257 session->filepath, session->monitor_context);
259 /* Close the handle */
260 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
261 session->read_handle = NULL;
263 /* Close the read file descriptor */
264 silc_file_close(session->fd);
268 /* Read more, until EOF is received */
269 session->read_offset += data_len;
270 silc_sftp_read(sftp, session->read_handle, session->read_offset,
271 SILC_PACKET_MAX_LEN - 1024,
272 silc_client_ftp_data, session);
274 /* Call monitor callback */
275 if (session->monitor)
276 (*session->monitor)(session->client, session->conn,
277 SILC_CLIENT_FILE_MONITOR_RECEIVE,
279 session->read_offset, session->filesize,
280 session->client_entry, session->session_id,
281 session->filepath, session->monitor_context);
283 /* Write the read data to the real file */
284 silc_file_write(session->fd, data, data_len);
287 /* Returns handle for the opened file. This is the downloader's function.
288 This will begin reading the data from the file. */
290 static void silc_client_ftp_open_handle(SilcSFTP sftp,
291 SilcSFTPStatus status,
292 SilcSFTPHandle handle,
295 SilcClientFtpSession session = (SilcClientFtpSession)context;
298 SILC_LOG_DEBUG(("Start"));
300 if (status != SILC_SFTP_STATUS_OK) {
301 /* Call monitor callback */
302 if (session->monitor)
303 (*session->monitor)(session->client, session->conn,
304 SILC_CLIENT_FILE_MONITOR_ERROR,
305 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
306 SILC_CLIENT_FILE_NO_SUCH_FILE :
307 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
308 SILC_CLIENT_FILE_PERMISSION_DENIED :
309 SILC_CLIENT_FILE_ERROR), 0, 0,
310 session->client_entry, session->session_id,
311 session->filepath, session->monitor_context);
315 /* Open the actual local file */
316 memset(path, 0, sizeof(path));
317 snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
318 session->path : "", session->filepath);
319 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
320 if (session->fd < 0) {
321 /* Call monitor callback */
322 session->client->internal->ops->say(session->client, session->conn,
323 SILC_CLIENT_MESSAGE_ERROR,
324 "File `%s' open failed: %s",
328 if (session->monitor)
329 (*session->monitor)(session->client, session->conn,
330 SILC_CLIENT_FILE_MONITOR_ERROR,
331 SILC_CLIENT_FILE_ERROR, 0, 0,
332 session->client_entry, session->session_id,
333 session->filepath, session->monitor_context);
337 session->read_handle = handle;
339 /* Now, start reading the file */
340 silc_sftp_read(sftp, session->read_handle, session->read_offset,
341 SILC_PACKET_MAX_LEN - 1024,
342 silc_client_ftp_data, session);
344 /* Call monitor callback */
345 if (session->monitor)
346 (*session->monitor)(session->client, session->conn,
347 SILC_CLIENT_FILE_MONITOR_RECEIVE,
349 session->read_offset, session->filesize,
350 session->client_entry, session->session_id,
351 session->filepath, session->monitor_context);
354 /* Returns the file name available for download. This is the downloader's
357 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
358 SilcSFTPStatus status,
359 const SilcSFTPName name,
362 SilcClientFtpSession session = (SilcClientFtpSession)context;
363 SilcSFTPAttributesStruct attr;
365 SILC_LOG_DEBUG(("Start"));
367 if (status != SILC_SFTP_STATUS_OK) {
368 /* Call monitor callback */
369 if (session->monitor)
370 (*session->monitor)(session->client, session->conn,
371 SILC_CLIENT_FILE_MONITOR_ERROR,
372 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
373 SILC_CLIENT_FILE_NO_SUCH_FILE :
374 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
375 SILC_CLIENT_FILE_PERMISSION_DENIED :
376 SILC_CLIENT_FILE_ERROR), 0, 0,
377 session->client_entry, session->session_id,
378 session->filepath, session->monitor_context);
382 /* Now open the file */
383 memset(&attr, 0, sizeof(attr));
384 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
385 silc_client_ftp_open_handle, session);
387 /* Save the important attributes like filename and file size */
388 session->filepath = strdup(name->filename[0]);
389 session->filesize = name->attrs[0]->size;
391 /* Close the directory handle */
392 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
393 session->dir_handle = NULL;
396 /* Returns the file handle after giving opendir command. This is the
397 downloader's function. */
399 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
400 SilcSFTPStatus status,
401 SilcSFTPHandle handle,
404 SilcClientFtpSession session = (SilcClientFtpSession)context;
406 SILC_LOG_DEBUG(("Start"));
408 if (status != SILC_SFTP_STATUS_OK) {
409 /* Call monitor callback */
410 if (session->monitor)
411 (*session->monitor)(session->client, session->conn,
412 SILC_CLIENT_FILE_MONITOR_ERROR,
413 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
414 SILC_CLIENT_FILE_NO_SUCH_FILE :
415 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
416 SILC_CLIENT_FILE_PERMISSION_DENIED :
417 SILC_CLIENT_FILE_ERROR), 0, 0,
418 session->client_entry, session->session_id,
419 session->filepath, session->monitor_context);
423 /* Now, read the directory */
424 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
425 session->dir_handle = handle;
428 /* SFTP version callback for SFTP client. This is the downloader's function
429 after initializing the SFTP connection to the remote client. This will
430 find out the filename available for download. */
432 static void silc_client_ftp_version(SilcSFTP sftp,
433 SilcSFTPStatus status,
434 SilcSFTPVersion version,
437 SilcClientFtpSession session = (SilcClientFtpSession)context;
439 SILC_LOG_DEBUG(("Start"));
441 if (status != SILC_SFTP_STATUS_OK) {
442 /* Call monitor callback */
443 if (session->monitor)
444 (*session->monitor)(session->client, session->conn,
445 SILC_CLIENT_FILE_MONITOR_ERROR,
446 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
447 SILC_CLIENT_FILE_NO_SUCH_FILE :
448 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
449 SILC_CLIENT_FILE_PERMISSION_DENIED :
450 SILC_CLIENT_FILE_ERROR), 0, 0,
451 session->client_entry, session->session_id,
452 session->filepath, session->monitor_context);
456 /* The SFTP session is open, now retrieve the info about available file. */
457 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
460 /* This callback is called after the key agreement protocol has been
461 performed. This calls the final completion callback for the application. */
463 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
465 SilcProtocol protocol = (SilcProtocol)context;
466 SilcClientKEInternalContext *ctx =
467 (SilcClientKEInternalContext *)protocol->context;
468 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
469 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
471 SILC_LOG_DEBUG(("Start"));
473 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
474 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
475 /* Call monitor callback */
476 if (session->monitor)
477 (*session->monitor)(session->client, session->conn,
478 SILC_CLIENT_FILE_MONITOR_ERROR,
479 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
480 session->client_entry, session->session_id,
481 session->filepath, session->monitor_context);
483 /* Error occured during protocol */
484 silc_ske_free_key_material(ctx->keymat);
488 /* Set keys into use */
489 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
490 ctx->ske->prop->cipher,
491 ctx->ske->prop->pkcs,
492 ctx->ske->prop->hash,
493 ctx->ske->prop->hmac,
494 ctx->ske->prop->group,
497 if (!session->server) {
498 /* If we are the SFTP client then start the SFTP session and retrieve
499 the info about the file available for download. */
500 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
501 session, silc_client_ftp_version,
504 /* Start SFTP server */
505 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
506 session, session->fs);
508 /* Monitor transmission */
509 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
510 silc_client_ftp_monitor, session);
513 /* Set this as active session */
514 conn->internal->active_session = session;
517 silc_ske_free_key_material(ctx->keymat);
519 silc_ske_free(ctx->ske);
520 silc_free(ctx->dest_id);
521 ctx->sock->protocol = NULL;
522 silc_socket_free(ctx->sock);
524 silc_protocol_free(protocol);
527 /* The downloader's function to start the key agreement protocol with the
528 remote client after we have connected to it. */
530 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
533 SilcClient client = session->client;
534 SilcClientKEInternalContext *proto_ctx;
535 SilcProtocol protocol;
536 SilcClientConnection conn;
539 SILC_LOG_DEBUG(("Start"));
541 /* Call monitor callback */
542 if (session->monitor)
543 (*session->monitor)(session->client, session->conn,
544 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
545 SILC_CLIENT_FILE_OK, 0, 0,
546 session->client_entry, session->session_id,
547 NULL, session->monitor_context);
549 /* Add new connection for this session */
550 conn = silc_client_add_connection(client, NULL, session->hostname,
551 session->port, session);
553 /* Allocate new socket connection object */
554 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
555 conn->sock->hostname = strdup(session->hostname);
556 conn->sock->port = silc_net_get_remote_port(sock);
557 session->sock = silc_socket_dup(conn->sock);
559 /* Allocate internal context for key exchange protocol. This is
560 sent as context for the protocol. */
561 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
562 proto_ctx->client = client;
563 proto_ctx->sock = silc_socket_dup(conn->sock);
564 proto_ctx->rng = client->rng;
565 proto_ctx->responder = FALSE;
566 proto_ctx->context = session;
567 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
568 proto_ctx->verify = silc_client_protocol_ke_verify_key;
570 /* Perform key exchange protocol. */
571 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
572 &protocol, (void *)proto_ctx,
573 silc_client_ftp_key_agreement_final);
574 conn->sock->protocol = protocol;
576 /* Register the connection for network input and output. This sets
577 that scheduler will listen for incoming packets for this connection
578 and sets that outgoing packets may be sent to this connection as well.
579 However, this doesn't set the scheduler for outgoing traffic, it will
580 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
581 later when outgoing data is available. */
582 context = (void *)client;
583 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
585 /* Execute the protocol */
586 silc_protocol_execute(protocol, client->schedule, 0, 0);
589 /* The remote client's (the client who made the file available for download)
590 function for accepting incoming connection. This will also start the
591 key agreement protocol with the other client. */
593 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
595 SilcClientFtpSession session = (SilcClientFtpSession)context;
596 SilcClient client = session->client;
597 SilcClientConnection conn;
598 SilcSocketConnection newsocket;
599 SilcClientKEInternalContext *proto_ctx;
602 SILC_LOG_DEBUG(("Start"));
604 sock = silc_net_accept_connection(session->listener);
606 /* Call monitor callback */
607 if (session->monitor)
608 (*session->monitor)(session->client, session->conn,
609 SILC_CLIENT_FILE_MONITOR_ERROR,
610 SILC_CLIENT_FILE_ERROR, 0, 0,
611 session->client_entry, session->session_id,
612 session->filepath, session->monitor_context);
616 /* Set socket options */
617 silc_net_set_socket_nonblock(sock);
618 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
620 /* Allocate new socket connection object */
621 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
623 /* Perform name and address lookups for the remote host. */
624 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
625 if (!newsocket->hostname && !newsocket->ip) {
626 /* Call monitor callback */
627 if (session->monitor)
628 (*session->monitor)(session->client, session->conn,
629 SILC_CLIENT_FILE_MONITOR_ERROR,
630 SILC_CLIENT_FILE_ERROR, 0, 0,
631 session->client_entry, session->session_id,
632 session->filepath, session->monitor_context);
635 if (!newsocket->hostname)
636 newsocket->hostname = strdup(newsocket->ip);
637 newsocket->port = silc_net_get_remote_port(sock);
639 /* Call monitor callback */
640 if (session->monitor)
641 (*session->monitor)(session->client, session->conn,
642 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
643 SILC_CLIENT_FILE_OK, 0, 0,
644 session->client_entry, session->session_id,
645 NULL, session->monitor_context);
647 /* Add new connection for this session */
648 conn = silc_client_add_connection(client, NULL, newsocket->hostname,
649 newsocket->port, session);
650 conn->sock = newsocket;
651 conn->sock->user_data = conn;
652 session->sock = silc_socket_dup(conn->sock);
654 /* Allocate internal context for key exchange protocol. This is
655 sent as context for the protocol. */
656 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
657 proto_ctx->client = client;
658 proto_ctx->sock = silc_socket_dup(conn->sock);
659 proto_ctx->rng = client->rng;
660 proto_ctx->responder = TRUE;
661 proto_ctx->context = session;
662 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
663 proto_ctx->verify = silc_client_protocol_ke_verify_key;
665 /* Prepare the connection for key exchange protocol. We allocate the
666 protocol but will not start it yet. The connector will be the
667 initiator of the protocol thus we will wait for initiation from
668 there before we start the protocol. */
669 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
670 &newsocket->protocol, proto_ctx,
671 silc_client_ftp_key_agreement_final);
673 /* Register the connection for network input and output. This sets
674 that scheduler will listen for incoming packets for this connection
675 and sets that outgoing packets may be sent to this connection as well.
676 However, this doesn't set the scheduler for outgoing traffic, it
677 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
678 later when outgoing data is available. */
679 context = (void *)client;
680 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
683 /* Free all file transfer sessions. */
685 void silc_client_ftp_free_sessions(SilcClient client,
686 SilcClientConnection conn)
688 if (conn->internal->ftp_sessions) {
689 SilcClientFtpSession session;
690 silc_dlist_start(conn->internal->ftp_sessions);
691 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
694 session->sock->user_data = NULL;
695 silc_client_ftp_session_free(session);
697 silc_dlist_del(conn->internal->ftp_sessions, session);
701 /* Free file transfer session by client entry. */
703 void silc_client_ftp_session_free_client(SilcClientConnection conn,
704 SilcClientEntry client_entry)
706 SilcClientFtpSession session;
708 if (!conn->internal->ftp_sessions)
711 /* Get the session */
712 silc_dlist_start(conn->internal->ftp_sessions);
713 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
715 if (session->client_entry == client_entry) {
717 session->sock->user_data = NULL;
718 silc_client_ftp_session_free(session);
723 /* Free session resources. */
725 void silc_client_ftp_session_free(SilcClientFtpSession session)
727 SilcClientConnection conn;
729 SILC_LOG_DEBUG(("Free session"));
731 if (session->conn && session->conn->internal->ftp_sessions)
732 silc_dlist_del(session->conn->internal->ftp_sessions, session);
734 if (session->conn && session->conn->internal->active_session == session)
735 session->conn->internal->active_session = NULL;
739 silc_sftp_server_shutdown(session->sftp);
741 silc_sftp_client_shutdown(session->sftp);
745 silc_sftp_fs_memory_free(session->fs);
747 /* Destroy listener */
748 if (session->listener) {
749 silc_schedule_unset_listen_fd(session->client->schedule,
751 silc_net_close_connection(session->listener);
752 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
755 /* Destroy session connection */
757 silc_schedule_unset_listen_fd(session->client->schedule,
758 session->sock->sock);
759 silc_net_close_connection(session->sock->sock);
761 if (session->sock->user_data) {
762 conn = (SilcClientConnection)session->sock->user_data;
764 if (conn->internal->active_session == session)
765 conn->internal->active_session = NULL;
767 silc_client_close_connection_real(session->client, session->sock, conn);
769 silc_socket_free(session->sock);
774 silc_buffer_free(session->packet);
776 silc_free(session->hostname);
777 silc_free(session->filepath);
778 silc_free(session->path);
779 memset(session, 'F', sizeof(*session));
783 /* Sends a file indicated by the `filepath' to the remote client
784 indicated by the `client_entry'. This will negotiate a secret key
785 with the remote client before actually starting the transmission of
786 the file. The `monitor' callback will be called to monitor the
787 transmission of the file. */
790 silc_client_file_send(SilcClient client,
791 SilcClientConnection conn,
792 SilcClientFileMonitor monitor,
793 void *monitor_context,
794 const char *local_ip,
795 SilcUInt32 local_port,
796 SilcClientEntry client_entry,
797 const char *filepath,
798 SilcUInt32 *session_id)
800 SilcClientFtpSession session;
801 SilcBuffer keyagr, ftp;
802 char *filename, *path;
805 SILC_LOG_DEBUG(("Start"));
807 /* Check for existing session for `filepath'. */
808 silc_dlist_start(conn->internal->ftp_sessions);
809 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
811 if (session->filepath && !strcmp(session->filepath, filepath) &&
812 session->client_entry == client_entry)
813 return SILC_CLIENT_FILE_ALREADY_STARTED;
816 /* See whether the file exists, and can be opened in generally speaking */
817 fd = silc_file_open(filepath, O_RDONLY);
819 return SILC_CLIENT_FILE_NO_SUCH_FILE;
822 /* Add new session */
823 session = silc_calloc(1, sizeof(*session));
824 session->session_id = ++conn->internal->next_session_id;
825 session->client = client;
826 session->conn = conn;
827 session->client_entry = client_entry;
828 session->monitor = monitor;
829 session->monitor_context = monitor_context;
830 session->filepath = strdup(filepath);
831 session->server = TRUE;
832 silc_dlist_add(conn->internal->ftp_sessions, session);
834 path = silc_calloc(strlen(filepath) + 9, sizeof(*path));
835 silc_strncat(path, strlen(filepath) + 9, "file://", 7);
836 silc_strncat(path, strlen(filepath) + 9, filepath, strlen(filepath));
838 /* Allocate memory filesystem and put the file to it */
839 if (strrchr(path, '/'))
840 filename = strrchr(path, '/') + 1;
842 filename = (char *)path;
843 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
844 SILC_SFTP_FS_PERM_EXEC);
845 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
848 session->filesize = silc_file_size(filepath);
850 /* Create the listener for incoming key exchange protocol. */
852 session->hostname = strdup(local_ip);
854 session->hostname = silc_net_localip();
855 session->listener = silc_net_create_server(local_port, session->hostname);
856 if (session->listener < 0) {
857 /* Could not create listener. Do the second best thing; send empty
858 key agreement packet and let the remote client provide the point
859 for the key exchange. */
860 SILC_LOG_DEBUG(("Could not create listener"));
861 silc_free(session->hostname);
862 session->hostname = NULL;
866 session->port = silc_net_get_local_port(session->listener);
867 silc_schedule_task_add(client->schedule, session->listener,
868 silc_client_ftp_process_key_agreement, session,
869 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
872 /* Send the key agreement inside FTP packet */
873 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
875 ftp = silc_buffer_alloc(1 + keyagr->len);
876 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
877 silc_buffer_format(ftp,
879 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
881 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
882 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
883 ftp->data, ftp->len, FALSE);
885 silc_buffer_free(keyagr);
886 silc_buffer_free(ftp);
890 *session_id = session->session_id;
892 return SILC_CLIENT_FILE_OK;
895 /* Receives a file from a client indicated by the `client_entry'. The
896 `session_id' indicates the file transmission session and it has been
897 received in the `ftp' client operation function. This will actually
898 perform the key agreement protocol with the remote client before
899 actually starting the file transmission. The `monitor' callback
900 will be called to monitor the transmission. */
903 silc_client_file_receive(SilcClient client,
904 SilcClientConnection conn,
905 SilcClientFileMonitor monitor,
906 void *monitor_context,
908 SilcUInt32 session_id)
910 SilcClientFtpSession session;
911 SilcBuffer keyagr, ftp;
913 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
915 /* Get the session */
916 silc_dlist_start(conn->internal->ftp_sessions);
917 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
919 if (session->session_id == session_id) {
924 if (session == SILC_LIST_END) {
925 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
926 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
929 /* See if we have this session running already */
930 if (session->sftp || session->listener) {
931 SILC_LOG_DEBUG(("Session already started"));
932 return SILC_CLIENT_FILE_ALREADY_STARTED;
935 session->monitor = monitor;
936 session->monitor_context = monitor_context;
937 session->conn = conn;
938 session->path = path ? strdup(path) : NULL;
940 /* If the hostname and port already exists then the remote client did
941 provide the connection point to us and we won't create listener, but
942 create the connection ourselves. */
943 if (session->hostname && session->port) {
944 if (silc_client_connect_to_client(client, conn, session->port,
945 session->hostname, session) < 0)
946 return SILC_CLIENT_FILE_ERROR;
948 /* Add the listener for the key agreement */
949 session->hostname = silc_net_localip();
950 session->listener = silc_net_create_server(0, session->hostname);
951 if (session->listener < 0) {
952 SILC_LOG_DEBUG(("Could not create listener"));
953 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
954 "Cannot create listener on %s: %s",
955 session->hostname, strerror(errno));
956 return SILC_CLIENT_FILE_ERROR;
958 session->port = silc_net_get_local_port(session->listener);
959 silc_schedule_task_add(client->schedule, session->listener,
960 silc_client_ftp_process_key_agreement, session,
961 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
963 /* Send the key agreement inside FTP packet */
964 keyagr = silc_key_agreement_payload_encode(session->hostname,
966 ftp = silc_buffer_alloc(1 + keyagr->len);
967 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
968 silc_buffer_format(ftp,
970 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
972 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
973 session->client_entry->id,
974 SILC_ID_CLIENT, NULL, NULL,
975 ftp->data, ftp->len, FALSE);
977 silc_buffer_free(keyagr);
978 silc_buffer_free(ftp);
981 return SILC_CLIENT_FILE_OK;
984 /* Closes file transmission session indicated by the `session_id'.
985 If file transmission is being conducted it will be aborted
986 automatically. This function is also used to close the session
987 after successful file transmission. This function can be used
988 also to reject incoming file transmission request. */
990 SilcClientFileError silc_client_file_close(SilcClient client,
991 SilcClientConnection conn,
992 SilcUInt32 session_id)
994 SilcClientFtpSession session;
996 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
998 /* Get the session */
999 silc_dlist_start(conn->internal->ftp_sessions);
1000 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1002 if (session->session_id == session_id) {
1007 if (session == SILC_LIST_END) {
1008 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1009 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1012 silc_client_ftp_session_free(session);
1014 return SILC_CLIENT_FILE_OK;
1017 /* Callback called after remote client information has been resolved.
1018 This will try to find existing session for the client entry. If found
1019 then continue with the key agreement protocol. If not then it means
1020 this is a file transfer request and we let the application know. */
1022 static void silc_client_ftp_resolve_cb(SilcClient client,
1023 SilcClientConnection conn,
1024 SilcClientEntry *clients,
1025 SilcUInt32 clients_count,
1028 SilcPacketContext *packet = (SilcPacketContext *)context;
1029 SilcClientFtpSession session;
1030 SilcKeyAgreementPayload payload = NULL;
1031 SilcClientEntry client_entry;
1035 SILC_LOG_DEBUG(("Start"));
1040 client_entry = clients[0];
1042 silc_dlist_start(conn->internal->ftp_sessions);
1043 while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1045 if (session->client_entry == client_entry && !session->server)
1049 /* Parse the key agreement payload */
1050 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1051 packet->buffer->len);
1055 hostname = silc_key_agreement_get_hostname(payload);
1056 port = silc_key_agreement_get_port(payload);
1062 if (session == SILC_LIST_END || (!hostname && !port)) {
1063 /* No session found, create one and let the application know about
1064 incoming file transfer request. */
1066 /* Add new session */
1067 session = silc_calloc(1, sizeof(*session));
1068 session->session_id = ++conn->internal->next_session_id;
1069 session->client = client;
1070 session->conn = conn;
1071 session->client_entry = client_entry;
1072 silc_dlist_add(conn->internal->ftp_sessions, session);
1074 /* Let the application know */
1075 client->internal->ops->ftp(client, conn, client_entry,
1076 session->session_id, hostname, port);
1078 if (hostname && port) {
1079 session->hostname = strdup(hostname);
1080 session->port = port;
1086 session->hostname = strdup(hostname);
1087 session->port = port;
1089 /* Session exists, continue with key agreement protocol. */
1090 if (silc_client_connect_to_client(client, conn, port,
1091 hostname, session) < 0) {
1092 /* Call monitor callback */
1093 if (session->monitor)
1094 (*session->monitor)(session->client, session->conn,
1095 SILC_CLIENT_FILE_MONITOR_ERROR,
1096 SILC_CLIENT_FILE_ERROR, 0, 0,
1097 session->client_entry, session->session_id,
1098 session->filepath, session->monitor_context);
1103 silc_key_agreement_payload_free(payload);
1104 silc_packet_context_free(packet);
1107 /* Called when file transfer packet is received. This will parse the
1108 packet and give it to the file transfer protocol. */
1110 void silc_client_ftp(SilcClient client,
1111 SilcSocketConnection sock,
1112 SilcPacketContext *packet)
1114 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1118 SILC_LOG_DEBUG(("Start"));
1120 /* Parse the payload */
1121 ret = silc_buffer_unformat(packet->buffer,
1122 SILC_STR_UI_CHAR(&type),
1127 /* We support only type number 1 (== SFTP) */
1131 silc_buffer_pull(packet->buffer, 1);
1133 /* If we have active FTP session then give the packet directly to the
1134 protocol processor. */
1135 if (conn->internal->active_session) {
1136 /* Give it to the SFTP */
1137 if (conn->internal->active_session->server)
1138 silc_sftp_server_receive_process(conn->internal->active_session->sftp,
1141 silc_sftp_client_receive_process(conn->internal->active_session->sftp,
1144 /* We don't have active session, resolve the remote client information
1145 and then try to find the correct session. */
1146 SilcClientID *remote_id;
1148 if (packet->src_id_type != SILC_ID_CLIENT)
1151 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1156 /* Resolve the client */
1157 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1158 NULL, silc_client_ftp_resolve_cb,
1159 silc_packet_context_dup(packet));
1160 silc_free(remote_id);