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 snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
317 session->path : "", session->filepath);
318 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
319 if (session->fd < 0) {
320 /* Call monitor callback */
321 session->client->internal->ops->say(session->client, session->conn,
322 SILC_CLIENT_MESSAGE_ERROR,
323 "File `%s' open failed: %s",
327 if (session->monitor)
328 (*session->monitor)(session->client, session->conn,
329 SILC_CLIENT_FILE_MONITOR_ERROR,
330 SILC_CLIENT_FILE_ERROR, 0, 0,
331 session->client_entry, session->session_id,
332 session->filepath, session->monitor_context);
336 session->read_handle = handle;
338 /* Now, start reading the file */
339 silc_sftp_read(sftp, session->read_handle, session->read_offset,
340 SILC_PACKET_MAX_LEN - 1024,
341 silc_client_ftp_data, session);
343 /* Call monitor callback */
344 if (session->monitor)
345 (*session->monitor)(session->client, session->conn,
346 SILC_CLIENT_FILE_MONITOR_RECEIVE,
348 session->read_offset, session->filesize,
349 session->client_entry, session->session_id,
350 session->filepath, session->monitor_context);
353 /* Returns the file name available for download. This is the downloader's
356 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
357 SilcSFTPStatus status,
358 const SilcSFTPName name,
361 SilcClientFtpSession session = (SilcClientFtpSession)context;
362 SilcSFTPAttributesStruct attr;
364 SILC_LOG_DEBUG(("Start"));
366 if (status != SILC_SFTP_STATUS_OK) {
367 /* Call monitor callback */
368 if (session->monitor)
369 (*session->monitor)(session->client, session->conn,
370 SILC_CLIENT_FILE_MONITOR_ERROR,
371 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
372 SILC_CLIENT_FILE_NO_SUCH_FILE :
373 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
374 SILC_CLIENT_FILE_PERMISSION_DENIED :
375 SILC_CLIENT_FILE_ERROR), 0, 0,
376 session->client_entry, session->session_id,
377 session->filepath, session->monitor_context);
381 /* Now open the file */
382 memset(&attr, 0, sizeof(attr));
383 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
384 silc_client_ftp_open_handle, session);
386 /* Save the important attributes like filename and file size */
387 session->filepath = strdup(name->filename[0]);
388 session->filesize = name->attrs[0]->size;
390 /* Close the directory handle */
391 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
392 session->dir_handle = NULL;
395 /* Returns the file handle after giving opendir command. This is the
396 downloader's function. */
398 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
399 SilcSFTPStatus status,
400 SilcSFTPHandle handle,
403 SilcClientFtpSession session = (SilcClientFtpSession)context;
405 SILC_LOG_DEBUG(("Start"));
407 if (status != SILC_SFTP_STATUS_OK) {
408 /* Call monitor callback */
409 if (session->monitor)
410 (*session->monitor)(session->client, session->conn,
411 SILC_CLIENT_FILE_MONITOR_ERROR,
412 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
413 SILC_CLIENT_FILE_NO_SUCH_FILE :
414 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
415 SILC_CLIENT_FILE_PERMISSION_DENIED :
416 SILC_CLIENT_FILE_ERROR), 0, 0,
417 session->client_entry, session->session_id,
418 session->filepath, session->monitor_context);
422 /* Now, read the directory */
423 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
424 session->dir_handle = handle;
427 /* SFTP version callback for SFTP client. This is the downloader's function
428 after initializing the SFTP connection to the remote client. This will
429 find out the filename available for download. */
431 static void silc_client_ftp_version(SilcSFTP sftp,
432 SilcSFTPStatus status,
433 SilcSFTPVersion version,
436 SilcClientFtpSession session = (SilcClientFtpSession)context;
438 SILC_LOG_DEBUG(("Start"));
440 if (status != SILC_SFTP_STATUS_OK) {
441 /* Call monitor callback */
442 if (session->monitor)
443 (*session->monitor)(session->client, session->conn,
444 SILC_CLIENT_FILE_MONITOR_ERROR,
445 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
446 SILC_CLIENT_FILE_NO_SUCH_FILE :
447 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
448 SILC_CLIENT_FILE_PERMISSION_DENIED :
449 SILC_CLIENT_FILE_ERROR), 0, 0,
450 session->client_entry, session->session_id,
451 session->filepath, session->monitor_context);
455 /* The SFTP session is open, now retrieve the info about available file. */
456 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
459 /* This callback is called after the key agreement protocol has been
460 performed. This calls the final completion callback for the application. */
462 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
464 SilcProtocol protocol = (SilcProtocol)context;
465 SilcClientKEInternalContext *ctx =
466 (SilcClientKEInternalContext *)protocol->context;
467 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
468 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
470 SILC_LOG_DEBUG(("Start"));
472 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
473 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
474 /* Call monitor callback */
475 if (session->monitor)
476 (*session->monitor)(session->client, session->conn,
477 SILC_CLIENT_FILE_MONITOR_ERROR,
478 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
479 session->client_entry, session->session_id,
480 session->filepath, session->monitor_context);
482 /* Error occured during protocol */
483 silc_ske_free_key_material(ctx->keymat);
487 /* Set keys into use */
488 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
489 ctx->ske->prop->cipher,
490 ctx->ske->prop->pkcs,
491 ctx->ske->prop->hash,
492 ctx->ske->prop->hmac,
493 ctx->ske->prop->group,
496 if (!session->server) {
497 /* If we are the SFTP client then start the SFTP session and retrieve
498 the info about the file available for download. */
499 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
500 session, silc_client_ftp_version,
503 /* Start SFTP server */
504 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
505 session, session->fs);
507 /* Monitor transmission */
508 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
509 silc_client_ftp_monitor, session);
512 /* Set this as active session */
513 conn->active_session = session;
516 silc_ske_free_key_material(ctx->keymat);
518 silc_ske_free(ctx->ske);
519 silc_free(ctx->dest_id);
520 ctx->sock->protocol = NULL;
521 silc_socket_free(ctx->sock);
523 silc_protocol_free(protocol);
526 /* The downloader's function to start the key agreement protocol with the
527 remote client after we have connected to it. */
529 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
532 SilcClient client = session->client;
533 SilcClientKEInternalContext *proto_ctx;
534 SilcProtocol protocol;
535 SilcClientConnection conn;
538 SILC_LOG_DEBUG(("Start"));
540 /* Call monitor callback */
541 if (session->monitor)
542 (*session->monitor)(session->client, session->conn,
543 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
544 SILC_CLIENT_FILE_OK, 0, 0,
545 session->client_entry, session->session_id,
546 NULL, session->monitor_context);
548 /* Add new connection for this session */
549 conn = silc_client_add_connection(client, NULL, session->hostname,
550 session->port, session);
552 /* Allocate new socket connection object */
553 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
554 conn->sock->hostname = strdup(session->hostname);
555 conn->sock->port = silc_net_get_remote_port(sock);
556 session->sock = silc_socket_dup(conn->sock);
558 /* Allocate internal context for key exchange protocol. This is
559 sent as context for the protocol. */
560 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
561 proto_ctx->client = client;
562 proto_ctx->sock = silc_socket_dup(conn->sock);
563 proto_ctx->rng = client->rng;
564 proto_ctx->responder = FALSE;
565 proto_ctx->context = session;
566 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
567 proto_ctx->verify = silc_client_protocol_ke_verify_key;
569 /* Perform key exchange protocol. */
570 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
571 &protocol, (void *)proto_ctx,
572 silc_client_ftp_key_agreement_final);
573 conn->sock->protocol = protocol;
575 /* Register the connection for network input and output. This sets
576 that scheduler will listen for incoming packets for this connection
577 and sets that outgoing packets may be sent to this connection as well.
578 However, this doesn't set the scheduler for outgoing traffic, it will
579 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
580 later when outgoing data is available. */
581 context = (void *)client;
582 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
584 /* Execute the protocol */
585 silc_protocol_execute(protocol, client->schedule, 0, 0);
588 /* The remote client's (the client who made the file available for download)
589 function for accepting incoming connection. This will also start the
590 key agreement protocol with the other client. */
592 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
594 SilcClientFtpSession session = (SilcClientFtpSession)context;
595 SilcClient client = session->client;
596 SilcClientConnection conn;
597 SilcSocketConnection newsocket;
598 SilcClientKEInternalContext *proto_ctx;
601 SILC_LOG_DEBUG(("Start"));
603 sock = silc_net_accept_connection(session->listener);
605 /* Call monitor callback */
606 if (session->monitor)
607 (*session->monitor)(session->client, session->conn,
608 SILC_CLIENT_FILE_MONITOR_ERROR,
609 SILC_CLIENT_FILE_ERROR, 0, 0,
610 session->client_entry, session->session_id,
611 session->filepath, session->monitor_context);
615 /* Set socket options */
616 silc_net_set_socket_nonblock(sock);
617 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
619 /* Allocate new socket connection object */
620 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
622 /* Perform name and address lookups for the remote host. */
623 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
624 if (!newsocket->hostname && !newsocket->ip) {
625 /* Call monitor callback */
626 if (session->monitor)
627 (*session->monitor)(session->client, session->conn,
628 SILC_CLIENT_FILE_MONITOR_ERROR,
629 SILC_CLIENT_FILE_ERROR, 0, 0,
630 session->client_entry, session->session_id,
631 session->filepath, session->monitor_context);
634 if (!newsocket->hostname)
635 newsocket->hostname = strdup(newsocket->ip);
636 newsocket->port = silc_net_get_remote_port(sock);
638 /* Call monitor callback */
639 if (session->monitor)
640 (*session->monitor)(session->client, session->conn,
641 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
642 SILC_CLIENT_FILE_OK, 0, 0,
643 session->client_entry, session->session_id,
644 NULL, session->monitor_context);
646 /* Add new connection for this session */
647 conn = silc_client_add_connection(client, NULL, newsocket->hostname,
648 newsocket->port, session);
649 conn->sock = newsocket;
650 conn->sock->user_data = conn;
651 session->sock = silc_socket_dup(conn->sock);
653 /* Allocate internal context for key exchange protocol. This is
654 sent as context for the protocol. */
655 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
656 proto_ctx->client = client;
657 proto_ctx->sock = silc_socket_dup(conn->sock);
658 proto_ctx->rng = client->rng;
659 proto_ctx->responder = TRUE;
660 proto_ctx->context = session;
661 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
662 proto_ctx->verify = silc_client_protocol_ke_verify_key;
664 /* Prepare the connection for key exchange protocol. We allocate the
665 protocol but will not start it yet. The connector will be the
666 initiator of the protocol thus we will wait for initiation from
667 there before we start the protocol. */
668 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
669 &newsocket->protocol, proto_ctx,
670 silc_client_ftp_key_agreement_final);
672 /* Register the connection for network input and output. This sets
673 that scheduler will listen for incoming packets for this connection
674 and sets that outgoing packets may be sent to this connection as well.
675 However, this doesn't set the scheduler for outgoing traffic, it
676 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
677 later when outgoing data is available. */
678 context = (void *)client;
679 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
682 /* Free all file transfer sessions. */
684 void silc_client_ftp_free_sessions(SilcClient client,
685 SilcClientConnection conn)
687 if (conn->ftp_sessions) {
688 SilcClientFtpSession session;
689 silc_dlist_start(conn->ftp_sessions);
690 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
692 session->sock->user_data = NULL;
693 silc_client_ftp_session_free(session);
695 silc_dlist_del(conn->ftp_sessions, session);
699 /* Free file transfer session by client entry. */
701 void silc_client_ftp_session_free_client(SilcClientConnection conn,
702 SilcClientEntry client_entry)
704 SilcClientFtpSession session;
706 if (!conn->ftp_sessions)
709 /* Get the session */
710 silc_dlist_start(conn->ftp_sessions);
711 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
712 if (session->client_entry == client_entry) {
714 session->sock->user_data = NULL;
715 silc_client_ftp_session_free(session);
720 /* Free session resources. */
722 void silc_client_ftp_session_free(SilcClientFtpSession session)
724 SilcClientConnection conn;
726 SILC_LOG_DEBUG(("Free session"));
728 if (session->conn && session->conn->ftp_sessions)
729 silc_dlist_del(session->conn->ftp_sessions, session);
731 if (session->conn && session->conn->active_session == session)
732 session->conn->active_session = NULL;
736 silc_sftp_server_shutdown(session->sftp);
738 silc_sftp_client_shutdown(session->sftp);
742 silc_sftp_fs_memory_free(session->fs);
744 /* Destroy listener */
745 if (session->listener) {
746 silc_schedule_unset_listen_fd(session->client->schedule,
748 silc_net_close_connection(session->listener);
749 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
752 /* Destroy session connection */
754 silc_schedule_unset_listen_fd(session->client->schedule,
755 session->sock->sock);
756 silc_net_close_connection(session->sock->sock);
758 if (session->sock->user_data) {
759 conn = (SilcClientConnection)session->sock->user_data;
761 if (conn->active_session == session)
762 conn->active_session = NULL;
764 silc_client_close_connection_real(session->client, session->sock, conn);
766 silc_socket_free(session->sock);
771 silc_buffer_free(session->packet);
773 silc_free(session->hostname);
774 silc_free(session->filepath);
775 silc_free(session->path);
776 memset(session, 'F', sizeof(*session));
780 /* Sends a file indicated by the `filepath' to the remote client
781 indicated by the `client_entry'. This will negotiate a secret key
782 with the remote client before actually starting the transmission of
783 the file. The `monitor' callback will be called to monitor the
784 transmission of the file. */
787 silc_client_file_send(SilcClient client,
788 SilcClientConnection conn,
789 SilcClientFileMonitor monitor,
790 void *monitor_context,
791 const char *local_ip,
792 SilcUInt32 local_port,
793 SilcClientEntry client_entry,
794 const char *filepath,
795 SilcUInt32 *session_id)
797 SilcClientFtpSession session;
798 SilcBuffer keyagr, ftp;
799 char *filename, *path;
802 SILC_LOG_DEBUG(("Start"));
804 /* Check for existing session for `filepath'. */
805 silc_dlist_start(conn->ftp_sessions);
806 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
807 if (session->filepath && !strcmp(session->filepath, filepath) &&
808 session->client_entry == client_entry)
809 return SILC_CLIENT_FILE_ALREADY_STARTED;
812 /* See whether the file exists, and can be opened in generally speaking */
813 fd = silc_file_open(filepath, O_RDONLY);
815 return SILC_CLIENT_FILE_NO_SUCH_FILE;
818 /* Add new session */
819 session = silc_calloc(1, sizeof(*session));
820 session->session_id = ++conn->next_session_id;
821 session->client = client;
822 session->conn = conn;
823 session->client_entry = client_entry;
824 session->monitor = monitor;
825 session->monitor_context = monitor_context;
826 session->filepath = strdup(filepath);
827 session->server = TRUE;
828 silc_dlist_add(conn->ftp_sessions, session);
830 path = silc_calloc(strlen(filepath) + 9, sizeof(*path));
831 silc_strncat(path, strlen(filepath) + 9, "file://", 7);
832 silc_strncat(path, strlen(filepath) + 9, filepath, strlen(filepath));
834 /* Allocate memory filesystem and put the file to it */
835 if (strrchr(path, '/'))
836 filename = strrchr(path, '/') + 1;
838 filename = (char *)path;
839 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
840 SILC_SFTP_FS_PERM_EXEC);
841 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
844 session->filesize = silc_file_size(filepath);
846 /* Create the listener for incoming key exchange protocol. */
848 session->hostname = strdup(local_ip);
850 session->hostname = silc_net_localip();
851 session->listener = silc_net_create_server(local_port, session->hostname);
852 if (session->listener < 0) {
853 /* Could not create listener. Do the second best thing; send empty
854 key agreement packet and let the remote client provide the point
855 for the key exchange. */
856 SILC_LOG_DEBUG(("Could not create listener"));
857 silc_free(session->hostname);
858 session->hostname = NULL;
862 session->port = silc_net_get_local_port(session->listener);
863 silc_schedule_task_add(client->schedule, session->listener,
864 silc_client_ftp_process_key_agreement, session,
865 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
868 /* Send the key agreement inside FTP packet */
869 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
871 ftp = silc_buffer_alloc(1 + keyagr->len);
872 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
873 silc_buffer_format(ftp,
875 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
877 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
878 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
879 ftp->data, ftp->len, FALSE);
881 silc_buffer_free(keyagr);
882 silc_buffer_free(ftp);
886 *session_id = session->session_id;
888 return SILC_CLIENT_FILE_OK;
891 /* Receives a file from a client indicated by the `client_entry'. The
892 `session_id' indicates the file transmission session and it has been
893 received in the `ftp' client operation function. This will actually
894 perform the key agreement protocol with the remote client before
895 actually starting the file transmission. The `monitor' callback
896 will be called to monitor the transmission. */
899 silc_client_file_receive(SilcClient client,
900 SilcClientConnection conn,
901 SilcClientFileMonitor monitor,
902 void *monitor_context,
904 SilcUInt32 session_id)
906 SilcClientFtpSession session;
907 SilcBuffer keyagr, ftp;
909 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
911 /* Get the session */
912 silc_dlist_start(conn->ftp_sessions);
913 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
914 if (session->session_id == session_id) {
919 if (session == SILC_LIST_END) {
920 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
921 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
924 /* See if we have this session running already */
925 if (session->sftp || session->listener) {
926 SILC_LOG_DEBUG(("Session already started"));
927 return SILC_CLIENT_FILE_ALREADY_STARTED;
930 session->monitor = monitor;
931 session->monitor_context = monitor_context;
932 session->conn = conn;
933 session->path = path ? strdup(path) : NULL;
935 /* If the hostname and port already exists then the remote client did
936 provide the connection point to us and we won't create listener, but
937 create the connection ourselves. */
938 if (session->hostname && session->port) {
939 if (silc_client_connect_to_client(client, conn, session->port,
940 session->hostname, session) < 0)
941 return SILC_CLIENT_FILE_ERROR;
943 /* Add the listener for the key agreement */
944 session->hostname = silc_net_localip();
945 session->listener = silc_net_create_server(0, session->hostname);
946 if (session->listener < 0) {
947 SILC_LOG_DEBUG(("Could not create listener"));
948 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
949 "Cannot create listener on %s: %s",
950 session->hostname, strerror(errno));
951 return SILC_CLIENT_FILE_ERROR;
953 session->port = silc_net_get_local_port(session->listener);
954 silc_schedule_task_add(client->schedule, session->listener,
955 silc_client_ftp_process_key_agreement, session,
956 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
958 /* Send the key agreement inside FTP packet */
959 keyagr = silc_key_agreement_payload_encode(session->hostname,
961 ftp = silc_buffer_alloc(1 + keyagr->len);
962 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
963 silc_buffer_format(ftp,
965 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
967 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
968 session->client_entry->id,
969 SILC_ID_CLIENT, NULL, NULL,
970 ftp->data, ftp->len, FALSE);
972 silc_buffer_free(keyagr);
973 silc_buffer_free(ftp);
976 return SILC_CLIENT_FILE_OK;
979 /* Closes file transmission session indicated by the `session_id'.
980 If file transmission is being conducted it will be aborted
981 automatically. This function is also used to close the session
982 after successful file transmission. This function can be used
983 also to reject incoming file transmission request. */
985 SilcClientFileError silc_client_file_close(SilcClient client,
986 SilcClientConnection conn,
987 SilcUInt32 session_id)
989 SilcClientFtpSession session;
991 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
993 /* Get the session */
994 silc_dlist_start(conn->ftp_sessions);
995 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
996 if (session->session_id == session_id) {
1001 if (session == SILC_LIST_END) {
1002 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1003 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1006 silc_client_ftp_session_free(session);
1008 return SILC_CLIENT_FILE_OK;
1011 /* Callback called after remote client information has been resolved.
1012 This will try to find existing session for the client entry. If found
1013 then continue with the key agreement protocol. If not then it means
1014 this is a file transfer request and we let the application know. */
1016 static void silc_client_ftp_resolve_cb(SilcClient client,
1017 SilcClientConnection conn,
1018 SilcClientEntry *clients,
1019 SilcUInt32 clients_count,
1022 SilcPacketContext *packet = (SilcPacketContext *)context;
1023 SilcClientFtpSession session;
1024 SilcKeyAgreementPayload payload = NULL;
1025 SilcClientEntry client_entry;
1029 SILC_LOG_DEBUG(("Start"));
1034 client_entry = clients[0];
1036 silc_dlist_start(conn->ftp_sessions);
1037 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1038 if (session->client_entry == client_entry && !session->server)
1042 /* Parse the key agreement payload */
1043 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1044 packet->buffer->len);
1048 hostname = silc_key_agreement_get_hostname(payload);
1049 port = silc_key_agreement_get_port(payload);
1055 if (session == SILC_LIST_END || (!hostname && !port)) {
1056 /* No session found, create one and let the application know about
1057 incoming file transfer request. */
1059 /* Add new session */
1060 session = silc_calloc(1, sizeof(*session));
1061 session->session_id = ++conn->next_session_id;
1062 session->client = client;
1063 session->conn = conn;
1064 session->client_entry = client_entry;
1065 silc_dlist_add(conn->ftp_sessions, session);
1067 /* Let the application know */
1068 client->internal->ops->ftp(client, conn, client_entry,
1069 session->session_id, hostname, port);
1071 if (hostname && port) {
1072 session->hostname = strdup(hostname);
1073 session->port = port;
1079 session->hostname = strdup(hostname);
1080 session->port = port;
1082 /* Session exists, continue with key agreement protocol. */
1083 if (silc_client_connect_to_client(client, conn, port,
1084 hostname, session) < 0) {
1085 /* Call monitor callback */
1086 if (session->monitor)
1087 (*session->monitor)(session->client, session->conn,
1088 SILC_CLIENT_FILE_MONITOR_ERROR,
1089 SILC_CLIENT_FILE_ERROR, 0, 0,
1090 session->client_entry, session->session_id,
1091 session->filepath, session->monitor_context);
1096 silc_key_agreement_payload_free(payload);
1097 silc_packet_context_free(packet);
1100 /* Called when file transfer packet is received. This will parse the
1101 packet and give it to the file transfer protocol. */
1103 void silc_client_ftp(SilcClient client,
1104 SilcSocketConnection sock,
1105 SilcPacketContext *packet)
1107 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1111 SILC_LOG_DEBUG(("Start"));
1113 /* Parse the payload */
1114 ret = silc_buffer_unformat(packet->buffer,
1115 SILC_STR_UI_CHAR(&type),
1120 /* We support only type number 1 (== SFTP) */
1124 silc_buffer_pull(packet->buffer, 1);
1126 /* If we have active FTP session then give the packet directly to the
1127 protocol processor. */
1128 if (conn->active_session) {
1129 /* Give it to the SFTP */
1130 if (conn->active_session->server)
1131 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1134 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1137 /* We don't have active session, resolve the remote client information
1138 and then try to find the correct session. */
1139 SilcClientID *remote_id;
1141 if (packet->src_id_type != SILC_ID_CLIENT)
1144 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1149 /* Resolve the client */
1150 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1151 NULL, silc_client_ftp_resolve_cb,
1152 silc_packet_context_dup(packet));
1153 silc_free(remote_id);