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);
140 silc_client_connect_to_client(SilcClient client,
141 SilcClientConnection conn, int port,
142 char *host, void *context)
144 SilcClientInternalConnectContext *ctx;
146 /* Allocate internal context for connection process. This is
147 needed as we are doing async connecting. */
148 ctx = silc_calloc(1, sizeof(*ctx));
149 ctx->client = client;
151 ctx->host = strdup(host);
154 ctx->context = context;
156 /* Do the actual connecting process */
157 return silc_client_connect_to_client_internal(ctx);
160 /* SFTP packet send callback. This will use preallocated buffer to avoid
161 reallocation of outgoing data buffer everytime. */
163 static void silc_client_ftp_send_packet(SilcBuffer packet, void *context)
165 SilcClientFtpSession session = (SilcClientFtpSession)context;
166 SilcClient client = session->client;
168 SILC_LOG_DEBUG(("Start"));
170 /* Allocate outgoing packet */
171 if (!session->packet)
172 session->packet = silc_buffer_alloc(1 + packet->len);
174 /* Enlarge outgoing packet if needed */
175 if (session->packet->truelen < 1 + packet->len)
176 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
179 silc_buffer_pull_tail(session->packet, 1 + packet->len);
180 silc_buffer_format(session->packet,
182 SILC_STR_UI_XNSTRING(packet->data, packet->len),
185 /* Send the packet immediately */
186 silc_client_packet_send(client, session->sock, SILC_PACKET_FTP, NULL,
187 0, NULL, NULL, session->packet->data,
188 session->packet->len, TRUE);
191 session->packet->data = session->packet->tail = session->packet->head;
192 session->packet->len = 0;
195 /* SFTP monitor callback for SFTP server. This reports the application
196 how the transmission is going along. This function is for the client
197 who made the file available for download. */
199 static void silc_client_ftp_monitor(SilcSFTP sftp,
200 SilcSFTPMonitors type,
201 const SilcSFTPMonitorData data,
204 SilcClientFtpSession session = (SilcClientFtpSession)context;
206 if (type == SILC_SFTP_MONITOR_READ) {
207 /* Call the monitor for application */
208 if (session->monitor)
209 (*session->monitor)(session->client, session->conn,
210 SILC_CLIENT_FILE_MONITOR_SEND,
212 data->offset, session->filesize,
213 session->client_entry, session->session_id,
214 session->filepath, session->monitor_context);
218 /* Returns the read data. This is the downloader's function (client side)
219 to receive the read data and read more until EOF is received from
220 the other side. This will also monitor the transmission and notify
223 static void silc_client_ftp_data(SilcSFTP sftp,
224 SilcSFTPStatus status,
225 const unsigned char *data,
229 SilcClientFtpSession session = (SilcClientFtpSession)context;
231 SILC_LOG_DEBUG(("Start"));
233 if (status == SILC_SFTP_STATUS_EOF) {
236 /* Close the handle */
237 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
238 session->read_handle = NULL;
240 /* Close the read file descriptor */
241 silc_file_close(session->fd);
245 if (status != SILC_SFTP_STATUS_OK) {
246 /* Call monitor callback */
247 if (session->monitor)
248 (*session->monitor)(session->client, session->conn,
249 SILC_CLIENT_FILE_MONITOR_ERROR,
250 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
251 SILC_CLIENT_FILE_NO_SUCH_FILE :
252 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
253 SILC_CLIENT_FILE_PERMISSION_DENIED :
254 SILC_CLIENT_FILE_ERROR), 0, 0,
255 session->client_entry, session->session_id,
256 session->filepath, session->monitor_context);
258 /* Close the handle */
259 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
260 session->read_handle = NULL;
262 /* Close the read file descriptor */
263 silc_file_close(session->fd);
267 /* Read more, until EOF is received */
268 session->read_offset += data_len;
269 silc_sftp_read(sftp, session->read_handle, session->read_offset,
270 SILC_PACKET_MAX_LEN - 1024,
271 silc_client_ftp_data, session);
273 /* Call monitor callback */
274 if (session->monitor)
275 (*session->monitor)(session->client, session->conn,
276 SILC_CLIENT_FILE_MONITOR_RECEIVE,
278 session->read_offset, session->filesize,
279 session->client_entry, session->session_id,
280 session->filepath, session->monitor_context);
282 /* Write the read data to the real file */
283 silc_file_write(session->fd, data, data_len);
286 /* Returns handle for the opened file. This is the downloader's function.
287 This will begin reading the data from the file. */
289 static void silc_client_ftp_open_handle(SilcSFTP sftp,
290 SilcSFTPStatus status,
291 SilcSFTPHandle handle,
294 SilcClientFtpSession session = (SilcClientFtpSession)context;
297 SILC_LOG_DEBUG(("Start"));
299 if (status != SILC_SFTP_STATUS_OK) {
300 /* Call monitor callback */
301 if (session->monitor)
302 (*session->monitor)(session->client, session->conn,
303 SILC_CLIENT_FILE_MONITOR_ERROR,
304 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
305 SILC_CLIENT_FILE_NO_SUCH_FILE :
306 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
307 SILC_CLIENT_FILE_PERMISSION_DENIED :
308 SILC_CLIENT_FILE_ERROR), 0, 0,
309 session->client_entry, session->session_id,
310 session->filepath, session->monitor_context);
314 /* Open the actual local file */
315 memset(path, 0, sizeof(path));
316 if (session->path && strlen(session->path) < sizeof(path))
317 strncat(path, session->path, strlen(session->path));
318 if (strlen(session->filepath) > sizeof(path) - strlen(path))
319 strncat(path, session->filepath, sizeof(path) - strlen(path) - 1);
321 strncat(path, session->filepath, strlen(session->filepath));
322 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
323 if (session->fd < 0) {
324 /* Call monitor callback */
325 session->client->internal->ops->say(session->client, session->conn,
326 SILC_CLIENT_MESSAGE_ERROR,
327 "File `%s' open failed: %s",
331 if (session->monitor)
332 (*session->monitor)(session->client, session->conn,
333 SILC_CLIENT_FILE_MONITOR_ERROR,
334 SILC_CLIENT_FILE_ERROR, 0, 0,
335 session->client_entry, session->session_id,
336 session->filepath, session->monitor_context);
340 session->read_handle = handle;
342 /* Now, start reading the file */
343 silc_sftp_read(sftp, session->read_handle, session->read_offset,
344 SILC_PACKET_MAX_LEN - 1024,
345 silc_client_ftp_data, session);
347 /* Call monitor callback */
348 if (session->monitor)
349 (*session->monitor)(session->client, session->conn,
350 SILC_CLIENT_FILE_MONITOR_RECEIVE,
352 session->read_offset, session->filesize,
353 session->client_entry, session->session_id,
354 session->filepath, session->monitor_context);
357 /* Returns the file name available for download. This is the downloader's
360 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
361 SilcSFTPStatus status,
362 const SilcSFTPName name,
365 SilcClientFtpSession session = (SilcClientFtpSession)context;
366 SilcSFTPAttributesStruct attr;
368 SILC_LOG_DEBUG(("Start"));
370 if (status != SILC_SFTP_STATUS_OK) {
371 /* Call monitor callback */
372 if (session->monitor)
373 (*session->monitor)(session->client, session->conn,
374 SILC_CLIENT_FILE_MONITOR_ERROR,
375 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
376 SILC_CLIENT_FILE_NO_SUCH_FILE :
377 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
378 SILC_CLIENT_FILE_PERMISSION_DENIED :
379 SILC_CLIENT_FILE_ERROR), 0, 0,
380 session->client_entry, session->session_id,
381 session->filepath, session->monitor_context);
385 /* Now open the file */
386 memset(&attr, 0, sizeof(attr));
387 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
388 silc_client_ftp_open_handle, session);
390 /* Save the important attributes like filename and file size */
391 session->filepath = strdup(name->filename[0]);
392 session->filesize = name->attrs[0]->size;
394 /* Close the directory handle */
395 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
396 session->dir_handle = NULL;
399 /* Returns the file handle after giving opendir command. This is the
400 downloader's function. */
402 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
403 SilcSFTPStatus status,
404 SilcSFTPHandle handle,
407 SilcClientFtpSession session = (SilcClientFtpSession)context;
409 SILC_LOG_DEBUG(("Start"));
411 if (status != SILC_SFTP_STATUS_OK) {
412 /* Call monitor callback */
413 if (session->monitor)
414 (*session->monitor)(session->client, session->conn,
415 SILC_CLIENT_FILE_MONITOR_ERROR,
416 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
417 SILC_CLIENT_FILE_NO_SUCH_FILE :
418 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
419 SILC_CLIENT_FILE_PERMISSION_DENIED :
420 SILC_CLIENT_FILE_ERROR), 0, 0,
421 session->client_entry, session->session_id,
422 session->filepath, session->monitor_context);
426 /* Now, read the directory */
427 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
428 session->dir_handle = handle;
431 /* SFTP version callback for SFTP client. This is the downloader's function
432 after initializing the SFTP connection to the remote client. This will
433 find out the filename available for download. */
435 static void silc_client_ftp_version(SilcSFTP sftp,
436 SilcSFTPStatus status,
437 SilcSFTPVersion version,
440 SilcClientFtpSession session = (SilcClientFtpSession)context;
442 SILC_LOG_DEBUG(("Start"));
444 if (status != SILC_SFTP_STATUS_OK) {
445 /* Call monitor callback */
446 if (session->monitor)
447 (*session->monitor)(session->client, session->conn,
448 SILC_CLIENT_FILE_MONITOR_ERROR,
449 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
450 SILC_CLIENT_FILE_NO_SUCH_FILE :
451 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
452 SILC_CLIENT_FILE_PERMISSION_DENIED :
453 SILC_CLIENT_FILE_ERROR), 0, 0,
454 session->client_entry, session->session_id,
455 session->filepath, session->monitor_context);
459 /* The SFTP session is open, now retrieve the info about available file. */
460 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
463 /* This callback is called after the key agreement protocol has been
464 performed. This calls the final completion callback for the application. */
466 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
468 SilcProtocol protocol = (SilcProtocol)context;
469 SilcClientKEInternalContext *ctx =
470 (SilcClientKEInternalContext *)protocol->context;
471 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
472 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
474 SILC_LOG_DEBUG(("Start"));
476 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
477 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
478 /* Call monitor callback */
479 if (session->monitor)
480 (*session->monitor)(session->client, session->conn,
481 SILC_CLIENT_FILE_MONITOR_ERROR,
482 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
483 session->client_entry, session->session_id,
484 session->filepath, session->monitor_context);
486 /* Error occured during protocol */
487 silc_ske_free_key_material(ctx->keymat);
491 /* Set keys into use */
492 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
493 ctx->ske->prop->cipher,
494 ctx->ske->prop->pkcs,
495 ctx->ske->prop->hash,
496 ctx->ske->prop->hmac,
497 ctx->ske->prop->group,
500 if (!session->server) {
501 /* If we are the SFTP client then start the SFTP session and retrieve
502 the info about the file available for download. */
503 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
504 session, silc_client_ftp_version,
507 /* Start SFTP server */
508 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
509 session, session->fs);
511 /* Monitor transmission */
512 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
513 silc_client_ftp_monitor, session);
516 /* Set this as active session */
517 conn->active_session = session;
520 silc_ske_free_key_material(ctx->keymat);
522 silc_ske_free(ctx->ske);
523 silc_free(ctx->dest_id);
524 ctx->sock->protocol = NULL;
525 silc_socket_free(ctx->sock);
527 silc_protocol_free(protocol);
530 /* The downloader's function to start the key agreement protocol with the
531 remote client after we have connected to it. */
533 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
536 SilcClient client = session->client;
537 SilcClientKEInternalContext *proto_ctx;
538 SilcProtocol protocol;
539 SilcClientConnection conn;
542 SILC_LOG_DEBUG(("Start"));
544 /* Call monitor callback */
545 if (session->monitor)
546 (*session->monitor)(session->client, session->conn,
547 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
548 SILC_CLIENT_FILE_OK, 0, 0,
549 session->client_entry, session->session_id,
550 NULL, session->monitor_context);
552 /* Add new connection for this session */
553 conn = silc_client_add_connection(client, NULL, session->hostname,
554 session->port, session);
556 /* Allocate new socket connection object */
557 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
558 conn->sock->hostname = strdup(session->hostname);
559 conn->sock->port = silc_net_get_remote_port(sock);
560 session->sock = silc_socket_dup(conn->sock);
562 /* Allocate internal context for key exchange protocol. This is
563 sent as context for the protocol. */
564 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
565 proto_ctx->client = client;
566 proto_ctx->sock = silc_socket_dup(conn->sock);
567 proto_ctx->rng = client->rng;
568 proto_ctx->responder = FALSE;
569 proto_ctx->context = session;
570 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
571 proto_ctx->verify = silc_client_protocol_ke_verify_key;
573 /* Perform key exchange protocol. */
574 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
575 &protocol, (void *)proto_ctx,
576 silc_client_ftp_key_agreement_final);
577 conn->sock->protocol = protocol;
579 /* Register the connection for network input and output. This sets
580 that scheduler will listen for incoming packets for this connection
581 and sets that outgoing packets may be sent to this connection as well.
582 However, this doesn't set the scheduler for outgoing traffic, it will
583 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
584 later when outgoing data is available. */
585 context = (void *)client;
586 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
588 /* Execute the protocol */
589 silc_protocol_execute(protocol, client->schedule, 0, 0);
592 /* The remote client's (the client who made the file available for download)
593 function for accepting incoming connection. This will also start the
594 key agreement protocol with the other client. */
596 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
598 SilcClientFtpSession session = (SilcClientFtpSession)context;
599 SilcClient client = session->client;
600 SilcClientConnection conn;
601 SilcSocketConnection newsocket;
602 SilcClientKEInternalContext *proto_ctx;
605 SILC_LOG_DEBUG(("Start"));
607 sock = silc_net_accept_connection(session->listener);
609 /* Call monitor callback */
610 if (session->monitor)
611 (*session->monitor)(session->client, session->conn,
612 SILC_CLIENT_FILE_MONITOR_ERROR,
613 SILC_CLIENT_FILE_ERROR, 0, 0,
614 session->client_entry, session->session_id,
615 session->filepath, session->monitor_context);
619 /* Set socket options */
620 silc_net_set_socket_nonblock(sock);
621 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
623 /* Allocate new socket connection object */
624 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
626 /* Perform name and address lookups for the remote host. */
627 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
628 if (!newsocket->hostname && !newsocket->ip) {
629 /* Call monitor callback */
630 if (session->monitor)
631 (*session->monitor)(session->client, session->conn,
632 SILC_CLIENT_FILE_MONITOR_ERROR,
633 SILC_CLIENT_FILE_ERROR, 0, 0,
634 session->client_entry, session->session_id,
635 session->filepath, session->monitor_context);
638 if (!newsocket->hostname)
639 newsocket->hostname = strdup(newsocket->ip);
640 newsocket->port = silc_net_get_remote_port(sock);
642 /* Call monitor callback */
643 if (session->monitor)
644 (*session->monitor)(session->client, session->conn,
645 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
646 SILC_CLIENT_FILE_OK, 0, 0,
647 session->client_entry, session->session_id,
648 NULL, session->monitor_context);
650 /* Add new connection for this session */
651 conn = silc_client_add_connection(client, NULL, newsocket->hostname,
652 newsocket->port, session);
653 conn->sock = newsocket;
654 conn->sock->user_data = conn;
655 session->sock = silc_socket_dup(conn->sock);
657 /* Allocate internal context for key exchange protocol. This is
658 sent as context for the protocol. */
659 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
660 proto_ctx->client = client;
661 proto_ctx->sock = silc_socket_dup(conn->sock);
662 proto_ctx->rng = client->rng;
663 proto_ctx->responder = TRUE;
664 proto_ctx->context = session;
665 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
666 proto_ctx->verify = silc_client_protocol_ke_verify_key;
668 /* Prepare the connection for key exchange protocol. We allocate the
669 protocol but will not start it yet. The connector will be the
670 initiator of the protocol thus we will wait for initiation from
671 there before we start the protocol. */
672 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
673 &newsocket->protocol, proto_ctx,
674 silc_client_ftp_key_agreement_final);
676 /* Register the connection for network input and output. This sets
677 that scheduler will listen for incoming packets for this connection
678 and sets that outgoing packets may be sent to this connection as well.
679 However, this doesn't set the scheduler for outgoing traffic, it
680 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
681 later when outgoing data is available. */
682 context = (void *)client;
683 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
686 /* Free all file transfer sessions. */
688 void silc_client_ftp_free_sessions(SilcClient client,
689 SilcClientConnection conn)
691 if (conn->ftp_sessions) {
692 SilcClientFtpSession session;
693 silc_dlist_start(conn->ftp_sessions);
694 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
696 session->sock->user_data = NULL;
697 silc_client_ftp_session_free(session);
699 silc_dlist_del(conn->ftp_sessions, session);
700 silc_dlist_uninit(conn->ftp_sessions);
704 /* Free file transfer session by client entry. */
706 void silc_client_ftp_session_free_client(SilcClientConnection conn,
707 SilcClientEntry client_entry)
709 SilcClientFtpSession session;
711 if (!conn->ftp_sessions)
714 /* Get the session */
715 silc_dlist_start(conn->ftp_sessions);
716 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
717 if (session->client_entry == client_entry) {
719 session->sock->user_data = NULL;
720 silc_client_ftp_session_free(session);
725 /* Free session resources. */
727 void silc_client_ftp_session_free(SilcClientFtpSession session)
729 SilcClientConnection conn;
731 SILC_LOG_DEBUG(("Free session"));
733 silc_dlist_del(session->conn->ftp_sessions, session);
737 silc_sftp_server_shutdown(session->sftp);
739 silc_sftp_client_shutdown(session->sftp);
743 silc_sftp_fs_memory_free(session->fs);
745 /* Destroy listener */
746 if (session->listener) {
747 silc_schedule_unset_listen_fd(session->client->schedule,
749 silc_net_close_connection(session->listener);
750 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
753 /* Destroy session connection */
755 silc_schedule_unset_listen_fd(session->client->schedule,
756 session->sock->sock);
757 silc_net_close_connection(session->sock->sock);
759 if (session->sock->user_data) {
760 conn = (SilcClientConnection)session->sock->user_data;
762 if (conn->active_session == session)
763 conn->active_session = NULL;
765 silc_client_close_connection_real(session->client, session->sock, conn);
767 silc_socket_free(session->sock);
772 silc_buffer_free(session->packet);
774 silc_free(session->hostname);
775 silc_free(session->filepath);
779 /* Sends a file indicated by the `filepath' to the remote client
780 indicated by the `client_entry'. This will negotiate a secret key
781 with the remote client before actually starting the transmission of
782 the file. The `monitor' callback will be called to monitor the
783 transmission of the file. */
786 silc_client_file_send(SilcClient client,
787 SilcClientConnection conn,
788 SilcClientFileMonitor monitor,
789 void *monitor_context,
790 const char *local_ip,
791 SilcUInt32 local_port,
792 SilcClientEntry client_entry,
793 const char *filepath,
794 SilcUInt32 *session_id)
796 SilcClientFtpSession session;
797 SilcBuffer keyagr, ftp;
798 char *filename, *path;
801 SILC_LOG_DEBUG(("Start"));
803 /* Check for existing session for `filepath'. */
804 silc_dlist_start(conn->ftp_sessions);
805 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
806 if (session->filepath && !strcmp(session->filepath, filepath) &&
807 session->client_entry == client_entry)
808 return SILC_CLIENT_FILE_ALREADY_STARTED;
811 /* See whether the file exists, and can be opened in generally speaking */
812 fd = silc_file_open(filepath, O_RDONLY);
814 return SILC_CLIENT_FILE_NO_SUCH_FILE;
817 /* Add new session */
818 session = silc_calloc(1, sizeof(*session));
819 session->session_id = ++conn->next_session_id;
820 session->client = client;
821 session->conn = conn;
822 session->client_entry = client_entry;
823 session->monitor = monitor;
824 session->monitor_context = monitor_context;
825 session->filepath = strdup(filepath);
826 session->server = TRUE;
827 silc_dlist_add(conn->ftp_sessions, session);
829 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
830 strcat(path, "file://");
831 strncat(path, filepath, strlen(filepath));
833 /* Allocate memory filesystem and put the file to it */
834 if (strrchr(path, '/'))
835 filename = strrchr(path, '/') + 1;
837 filename = (char *)path;
838 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
839 SILC_SFTP_FS_PERM_EXEC);
840 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
843 session->filesize = silc_file_size(filepath);
845 /* Create the listener for incoming key exchange protocol. */
847 session->hostname = strdup(local_ip);
849 session->hostname = silc_net_localip();
850 session->listener = silc_net_create_server(local_port, session->hostname);
851 if (session->listener < 0) {
852 /* Could not create listener. Do the second best thing; send empty
853 key agreement packet and let the remote client provide the point
854 for the key exchange. */
855 SILC_LOG_DEBUG(("Could not create listener"));
856 silc_free(session->hostname);
857 session->hostname = NULL;
861 session->port = silc_net_get_local_port(session->listener);
862 silc_schedule_task_add(client->schedule, session->listener,
863 silc_client_ftp_process_key_agreement, session,
864 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
867 /* Send the key agreement inside FTP packet */
868 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
870 ftp = silc_buffer_alloc(1 + keyagr->len);
871 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
872 silc_buffer_format(ftp,
874 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
876 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
877 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
878 ftp->data, ftp->len, FALSE);
880 silc_buffer_free(keyagr);
881 silc_buffer_free(ftp);
885 *session_id = session->session_id;
887 return SILC_CLIENT_FILE_OK;
890 /* Receives a file from a client indicated by the `client_entry'. The
891 `session_id' indicates the file transmission session and it has been
892 received in the `ftp' client operation function. This will actually
893 perform the key agreement protocol with the remote client before
894 actually starting the file transmission. The `monitor' callback
895 will be called to monitor the transmission. */
898 silc_client_file_receive(SilcClient client,
899 SilcClientConnection conn,
900 SilcClientFileMonitor monitor,
901 void *monitor_context,
903 SilcUInt32 session_id)
905 SilcClientFtpSession session;
906 SilcBuffer keyagr, ftp;
908 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
910 /* Get the session */
911 silc_dlist_start(conn->ftp_sessions);
912 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
913 if (session->session_id == session_id) {
918 if (session == SILC_LIST_END) {
919 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
920 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
923 /* See if we have this session running already */
924 if (session->sftp || session->listener) {
925 SILC_LOG_DEBUG(("Session already started"));
926 return SILC_CLIENT_FILE_ALREADY_STARTED;
929 session->monitor = monitor;
930 session->monitor_context = monitor_context;
931 session->conn = conn;
932 session->path = path ? strdup(path) : NULL;
934 /* If the hostname and port already exists then the remote client did
935 provide the connection point to us and we won't create listener, but
936 create the connection ourselves. */
937 if (session->hostname && session->port) {
938 if (silc_client_connect_to_client(client, conn, session->port,
939 session->hostname, session) < 0)
940 return SILC_CLIENT_FILE_ERROR;
942 /* Add the listener for the key agreement */
943 session->hostname = silc_net_localip();
944 session->listener = silc_net_create_server(0, session->hostname);
945 if (session->listener < 0) {
946 SILC_LOG_DEBUG(("Could not create listener"));
947 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
948 "Cannot create listener on %s: %s",
949 session->hostname, strerror(errno));
950 return SILC_CLIENT_FILE_ERROR;
952 session->port = silc_net_get_local_port(session->listener);
953 silc_schedule_task_add(client->schedule, session->listener,
954 silc_client_ftp_process_key_agreement, session,
955 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
957 /* Send the key agreement inside FTP packet */
958 keyagr = silc_key_agreement_payload_encode(session->hostname,
960 ftp = silc_buffer_alloc(1 + keyagr->len);
961 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
962 silc_buffer_format(ftp,
964 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
966 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
967 session->client_entry->id,
968 SILC_ID_CLIENT, NULL, NULL,
969 ftp->data, ftp->len, FALSE);
971 silc_buffer_free(keyagr);
972 silc_buffer_free(ftp);
975 return SILC_CLIENT_FILE_OK;
978 /* Closes file transmission session indicated by the `session_id'.
979 If file transmission is being conducted it will be aborted
980 automatically. This function is also used to close the session
981 after successful file transmission. This function can be used
982 also to reject incoming file transmission request. */
984 SilcClientFileError silc_client_file_close(SilcClient client,
985 SilcClientConnection conn,
986 SilcUInt32 session_id)
988 SilcClientFtpSession session;
990 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
992 /* Get the session */
993 silc_dlist_start(conn->ftp_sessions);
994 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
995 if (session->session_id == session_id) {
1000 if (session == SILC_LIST_END) {
1001 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1002 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1005 silc_client_ftp_session_free(session);
1007 return SILC_CLIENT_FILE_OK;
1010 /* Callback called after remote client information has been resolved.
1011 This will try to find existing session for the client entry. If found
1012 then continue with the key agreement protocol. If not then it means
1013 this is a file transfer request and we let the application know. */
1015 static void silc_client_ftp_resolve_cb(SilcClient client,
1016 SilcClientConnection conn,
1017 SilcClientEntry *clients,
1018 SilcUInt32 clients_count,
1021 SilcPacketContext *packet = (SilcPacketContext *)context;
1022 SilcClientFtpSession session;
1023 SilcKeyAgreementPayload payload = NULL;
1024 SilcClientEntry client_entry;
1028 SILC_LOG_DEBUG(("Start"));
1033 client_entry = clients[0];
1035 silc_dlist_start(conn->ftp_sessions);
1036 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1037 if (session->client_entry == client_entry)
1041 /* Parse the key agreement payload */
1042 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1043 packet->buffer->len);
1047 hostname = silc_key_agreement_get_hostname(payload);
1048 port = silc_key_agreement_get_port(payload);
1054 if (session == SILC_LIST_END || (!hostname && !port)) {
1055 /* No session found, create one and let the application know about
1056 incoming file transfer request. */
1058 /* Add new session */
1059 session = silc_calloc(1, sizeof(*session));
1060 session->session_id = ++conn->next_session_id;
1061 session->client = client;
1062 session->conn = conn;
1063 session->client_entry = client_entry;
1064 silc_dlist_add(conn->ftp_sessions, session);
1066 /* Let the application know */
1067 client->internal->ops->ftp(client, conn, client_entry,
1068 session->session_id, hostname, port);
1070 if (hostname && port) {
1071 session->hostname = strdup(hostname);
1072 session->port = port;
1078 session->hostname = strdup(hostname);
1079 session->port = port;
1081 /* Session exists, continue with key agreement protocol. */
1082 if (silc_client_connect_to_client(client, conn, port,
1083 hostname, session) < 0) {
1084 /* Call monitor callback */
1085 if (session->monitor)
1086 (*session->monitor)(session->client, session->conn,
1087 SILC_CLIENT_FILE_MONITOR_ERROR,
1088 SILC_CLIENT_FILE_ERROR, 0, 0,
1089 session->client_entry, session->session_id,
1090 session->filepath, session->monitor_context);
1095 silc_key_agreement_payload_free(payload);
1096 silc_packet_context_free(packet);
1099 /* Called when file transfer packet is received. This will parse the
1100 packet and give it to the file transfer protocol. */
1102 void silc_client_ftp(SilcClient client,
1103 SilcSocketConnection sock,
1104 SilcPacketContext *packet)
1106 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1110 SILC_LOG_DEBUG(("Start"));
1112 /* Parse the payload */
1113 ret = silc_buffer_unformat(packet->buffer,
1114 SILC_STR_UI_CHAR(&type),
1119 /* We support only type number 1 (== SFTP) */
1123 silc_buffer_pull(packet->buffer, 1);
1125 /* If we have active FTP session then give the packet directly to the
1126 protocol processor. */
1127 if (conn->active_session) {
1128 /* Give it to the SFTP */
1129 if (conn->active_session->server)
1130 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1133 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1136 /* We don't have active session, resolve the remote client information
1137 and then try to find the correct session. */
1138 SilcClientID *remote_id;
1140 if (packet->src_id_type != SILC_ID_CLIENT)
1143 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1148 /* Resolve the client */
1149 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1150 silc_client_ftp_resolve_cb,
1151 silc_packet_context_dup(packet));
1152 silc_free(remote_id);