5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "clientlibincludes.h"
22 #include "client_internal.h"
25 silc_client_connect_to_client(SilcClient client,
26 SilcClientConnection conn, int port,
27 char *host, void *context);
29 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
30 SILC_TASK_CALLBACK(silc_client_ftp_connected);
31 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
34 /* File transmission session */
35 struct SilcClientFtpSessionStruct {
38 SilcClientConnection conn;
39 SilcClientEntry client_entry;
41 SilcSocketConnection sock;
48 SilcClientFileMonitor monitor;
49 void *monitor_context;
53 SilcSFTPFilesystem fs;
56 SilcSFTPHandle dir_handle;
57 SilcSFTPHandle read_handle;
63 SILC_TASK_CALLBACK(silc_client_ftp_connected)
65 SilcClientInternalConnectContext *ctx =
66 (SilcClientInternalConnectContext *)context;
67 SilcClient client = ctx->client;
68 SilcClientConnection conn = ctx->conn;
69 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
70 int opt, opt_len = sizeof(opt);
72 SILC_LOG_DEBUG(("Start"));
74 /* Check the socket status as it might be in error */
75 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
78 /* Connection failed but lets try again */
79 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
80 "Could not connect to client %s: %s",
81 ctx->host, strerror(opt));
82 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
83 "Connecting to port %d of client %s resumed",
84 ctx->port, ctx->host);
86 /* Unregister old connection try */
87 silc_schedule_unset_listen_fd(client->schedule, fd);
88 silc_net_close_connection(fd);
89 silc_schedule_task_del(client->schedule, ctx->task);
92 silc_client_connect_to_client_internal(ctx);
95 /* Connection failed and we won't try anymore */
96 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
97 "Could not connect to client %s: %s",
98 ctx->host, strerror(opt));
99 silc_schedule_unset_listen_fd(client->schedule, fd);
100 silc_net_close_connection(fd);
101 silc_schedule_task_del(client->schedule, ctx->task);
103 silc_client_ftp_session_free(session);
108 silc_schedule_unset_listen_fd(client->schedule, fd);
109 silc_schedule_task_del(client->schedule, ctx->task);
111 /* Start the key agreement */
112 silc_client_ftp_start_key_agreement(session, fd);
116 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
120 /* Create connection to server asynchronously */
121 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
125 /* Register task that will receive the async connect and will
127 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
128 silc_client_ftp_connected,
131 SILC_TASK_PRI_NORMAL);
132 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
138 silc_client_connect_to_client(SilcClient client,
139 SilcClientConnection conn, int port,
140 char *host, void *context)
142 SilcClientInternalConnectContext *ctx;
144 /* Allocate internal context for connection process. This is
145 needed as we are doing async connecting. */
146 ctx = silc_calloc(1, sizeof(*ctx));
147 ctx->client = client;
149 ctx->host = strdup(host);
152 ctx->context = context;
154 /* Do the actual connecting process */
155 return silc_client_connect_to_client_internal(ctx);
158 /* SFTP packet send callback. This will use preallocated buffer to avoid
159 reallocation of outgoing data buffer everytime. */
161 static void silc_client_ftp_send_packet(SilcSocketConnection sock,
162 SilcBuffer packet, void *context)
164 SilcClientFtpSession session = (SilcClientFtpSession)context;
165 SilcClient client = session->client;
167 SILC_LOG_DEBUG(("Start"));
169 /* Allocate outgoing packet */
170 if (!session->packet)
171 session->packet = silc_buffer_alloc(1 + packet->len);
173 /* Enlarge outgoing packet if needed */
174 if (session->packet->truelen < 1 + packet->len)
175 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
178 silc_buffer_pull_tail(session->packet, 1 + packet->len);
179 silc_buffer_format(session->packet,
181 SILC_STR_UI_XNSTRING(packet->data, packet->len),
184 /* Send the packet immediately */
185 silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
186 session->packet->data, session->packet->len, TRUE);
189 session->packet->data = session->packet->tail = session->packet->head;
190 session->packet->len = 0;
193 /* SFTP monitor callback for SFTP server. This reports the application
194 how the transmission is going along. This function is for the client
195 who made the file available for download. */
197 static void silc_client_ftp_monitor(SilcSFTP sftp,
198 SilcSFTPMonitors type,
199 const SilcSFTPMonitorData data,
202 SilcClientFtpSession session = (SilcClientFtpSession)context;
204 if (type == SILC_SFTP_MONITOR_READ) {
205 /* Call the monitor for application */
206 if (session->monitor)
207 (*session->monitor)(session->client, session->conn,
208 SILC_CLIENT_FILE_MONITOR_SEND,
210 data->offset, session->filesize,
211 session->client_entry, session->session_id,
212 session->filepath, session->monitor_context);
216 /* Returns the read data. This is the downloader's function (client side)
217 to receive the read data and read more until EOF is received from
218 the other side. This will also monitor the transmission and notify
221 static void silc_client_ftp_data(SilcSFTP sftp,
222 SilcSFTPStatus status,
223 const unsigned char *data,
227 SilcClientFtpSession session = (SilcClientFtpSession)context;
229 SILC_LOG_DEBUG(("Start"));
231 if (status == SILC_SFTP_STATUS_EOF) {
234 /* Close the handle */
235 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
236 session->read_handle = NULL;
238 /* Close the read file descriptor */
239 silc_file_close(session->fd);
243 if (status != SILC_SFTP_STATUS_OK) {
244 /* Call monitor callback */
245 if (session->monitor)
246 (*session->monitor)(session->client, session->conn,
247 SILC_CLIENT_FILE_MONITOR_ERROR,
248 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
249 SILC_CLIENT_FILE_NO_SUCH_FILE :
250 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
251 SILC_CLIENT_FILE_PERMISSION_DENIED :
252 SILC_CLIENT_FILE_ERROR), 0, 0,
253 session->client_entry, session->session_id,
254 session->filepath, session->monitor_context);
256 /* Close the handle */
257 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
258 session->read_handle = NULL;
260 /* Close the read file descriptor */
261 silc_file_close(session->fd);
265 /* Read more, until EOF is received */
266 session->read_offset += data_len;
267 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
268 silc_client_ftp_data, session);
270 /* Call monitor callback */
271 if (session->monitor)
272 (*session->monitor)(session->client, session->conn,
273 SILC_CLIENT_FILE_MONITOR_RECEIVE,
275 session->read_offset, session->filesize,
276 session->client_entry, session->session_id,
277 session->filepath, session->monitor_context);
279 /* Write the read data to the real file */
280 silc_file_write(session->fd, data, data_len);
283 /* Returns handle for the opened file. This is the downloader's function.
284 This will begin reading the data from the file. */
286 static void silc_client_ftp_open_handle(SilcSFTP sftp,
287 SilcSFTPStatus status,
288 SilcSFTPHandle handle,
291 SilcClientFtpSession session = (SilcClientFtpSession)context;
293 SILC_LOG_DEBUG(("Start"));
295 if (status != SILC_SFTP_STATUS_OK) {
296 /* Call monitor callback */
297 if (session->monitor)
298 (*session->monitor)(session->client, session->conn,
299 SILC_CLIENT_FILE_MONITOR_ERROR,
300 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
301 SILC_CLIENT_FILE_NO_SUCH_FILE :
302 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
303 SILC_CLIENT_FILE_PERMISSION_DENIED :
304 SILC_CLIENT_FILE_ERROR), 0, 0,
305 session->client_entry, session->session_id,
306 session->filepath, session->monitor_context);
310 /* Open the actual local file */
311 session->fd = silc_file_open(session->filepath,
312 O_RDWR | O_CREAT | O_EXCL);
313 if (session->fd < 0) {
314 /* Call monitor callback */
315 session->client->ops->say(session->client, session->conn,
316 SILC_CLIENT_MESSAGE_ERROR,
317 "File `%s' open failed: %s", session->filepath,
320 if (session->monitor)
321 (*session->monitor)(session->client, session->conn,
322 SILC_CLIENT_FILE_MONITOR_ERROR,
323 SILC_CLIENT_FILE_ERROR, 0, 0,
324 session->client_entry, session->session_id,
325 session->filepath, session->monitor_context);
329 session->read_handle = handle;
331 /* Now, start reading the file */
332 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
333 silc_client_ftp_data, session);
335 /* Call monitor callback */
336 if (session->monitor)
337 (*session->monitor)(session->client, session->conn,
338 SILC_CLIENT_FILE_MONITOR_RECEIVE,
340 session->read_offset, session->filesize,
341 session->client_entry, session->session_id,
342 session->filepath, session->monitor_context);
345 /* Returns the file name available for download. This is the downloader's
348 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
349 SilcSFTPStatus status,
350 const SilcSFTPName name,
353 SilcClientFtpSession session = (SilcClientFtpSession)context;
354 SilcSFTPAttributesStruct attr;
356 SILC_LOG_DEBUG(("Start"));
358 if (status != SILC_SFTP_STATUS_OK) {
359 /* Call monitor callback */
360 if (session->monitor)
361 (*session->monitor)(session->client, session->conn,
362 SILC_CLIENT_FILE_MONITOR_ERROR,
363 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
364 SILC_CLIENT_FILE_NO_SUCH_FILE :
365 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
366 SILC_CLIENT_FILE_PERMISSION_DENIED :
367 SILC_CLIENT_FILE_ERROR), 0, 0,
368 session->client_entry, session->session_id,
369 session->filepath, session->monitor_context);
373 /* Now open the file */
374 memset(&attr, 0, sizeof(attr));
375 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
376 silc_client_ftp_open_handle, session);
378 /* Save the important attributes */
379 session->filepath = strdup(name->filename[0]);
380 session->filesize = name->attrs[0]->size;
382 /* Close the directory handle */
383 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
384 session->dir_handle = NULL;
387 /* Returns the file handle after giving opendir command. This is the
388 downloader's function. */
390 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
391 SilcSFTPStatus status,
392 SilcSFTPHandle handle,
395 SilcClientFtpSession session = (SilcClientFtpSession)context;
397 SILC_LOG_DEBUG(("Start"));
399 if (status != SILC_SFTP_STATUS_OK) {
400 /* Call monitor callback */
401 if (session->monitor)
402 (*session->monitor)(session->client, session->conn,
403 SILC_CLIENT_FILE_MONITOR_ERROR,
404 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
405 SILC_CLIENT_FILE_NO_SUCH_FILE :
406 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
407 SILC_CLIENT_FILE_PERMISSION_DENIED :
408 SILC_CLIENT_FILE_ERROR), 0, 0,
409 session->client_entry, session->session_id,
410 session->filepath, session->monitor_context);
414 /* Now, read the directory */
415 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
416 session->dir_handle = handle;
419 /* SFTP version callback for SFTP client. This is the downloader's function
420 after initializing the SFTP connection to the remote client. This will
421 find out the filename available for download. */
423 static void silc_client_ftp_version(SilcSFTP sftp,
424 SilcSFTPStatus status,
425 SilcSFTPVersion version,
428 SilcClientFtpSession session = (SilcClientFtpSession)context;
430 SILC_LOG_DEBUG(("Start"));
432 if (status != SILC_SFTP_STATUS_OK) {
433 /* Call monitor callback */
434 if (session->monitor)
435 (*session->monitor)(session->client, session->conn,
436 SILC_CLIENT_FILE_MONITOR_ERROR,
437 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
438 SILC_CLIENT_FILE_NO_SUCH_FILE :
439 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
440 SILC_CLIENT_FILE_PERMISSION_DENIED :
441 SILC_CLIENT_FILE_ERROR), 0, 0,
442 session->client_entry, session->session_id,
443 session->filepath, session->monitor_context);
447 /* The SFTP session is open, now retrieve the info about available file. */
448 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
451 /* This callback is called after the key agreement protocol has been
452 performed. This calls the final completion callback for the application. */
454 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
456 SilcProtocol protocol = (SilcProtocol)context;
457 SilcClientKEInternalContext *ctx =
458 (SilcClientKEInternalContext *)protocol->context;
459 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
460 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
462 SILC_LOG_DEBUG(("Start"));
464 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
465 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
466 /* Error occured during protocol */
467 silc_ske_free_key_material(ctx->keymat);
471 /* Set keys into use */
472 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
473 ctx->ske->prop->cipher,
474 ctx->ske->prop->pkcs,
475 ctx->ske->prop->hash,
476 ctx->ske->prop->hmac,
477 ctx->ske->prop->group,
480 if (!session->server) {
481 /* If we are the SFTP client then start the SFTP session and retrieve
482 the info about the file available for download. */
483 session->sftp = silc_sftp_client_start(conn->sock,
484 silc_client_ftp_send_packet,
486 silc_client_ftp_version, session);
488 /* Start SFTP server */
489 session->sftp = silc_sftp_server_start(conn->sock,
490 silc_client_ftp_send_packet,
491 session, session->fs);
493 /* Monitor transmission */
494 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
495 silc_client_ftp_monitor, session);
498 /* Set this as active session */
499 conn->active_session = session;
502 silc_ske_free_key_material(ctx->keymat);
504 silc_ske_free(ctx->ske);
505 silc_free(ctx->dest_id);
506 ctx->sock->protocol = NULL;
507 silc_socket_free(ctx->sock);
509 silc_protocol_free(protocol);
512 /* The downloader's function to start the key agreement protocol with the
513 remote client after we have connected to it. */
515 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
518 SilcClient client = session->client;
519 SilcClientKEInternalContext *proto_ctx;
520 SilcProtocol protocol;
521 SilcClientConnection conn;
524 SILC_LOG_DEBUG(("Start"));
526 /* Call monitor callback */
527 if (session->monitor)
528 (*session->monitor)(session->client, session->conn,
529 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
530 SILC_CLIENT_FILE_OK, 0, 0,
531 session->client_entry, session->session_id,
532 NULL, session->monitor_context);
534 /* Add new connection for this session */
535 conn = silc_client_add_connection(client, session->hostname,
536 session->port, session);
538 /* Allocate new socket connection object */
539 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
540 conn->sock->hostname = strdup(session->hostname);
541 conn->sock->port = silc_net_get_remote_port(sock);
542 session->sock = silc_socket_dup(conn->sock);
544 /* Allocate internal context for key exchange protocol. This is
545 sent as context for the protocol. */
546 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
547 proto_ctx->client = client;
548 proto_ctx->sock = silc_socket_dup(conn->sock);
549 proto_ctx->rng = client->rng;
550 proto_ctx->responder = FALSE;
551 proto_ctx->context = session;
552 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
553 proto_ctx->verify = silc_client_protocol_ke_verify_key;
555 /* Perform key exchange protocol. */
556 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
557 &protocol, (void *)proto_ctx,
558 silc_client_ftp_key_agreement_final);
559 conn->sock->protocol = protocol;
561 /* Register the connection for network input and output. This sets
562 that scheduler will listen for incoming packets for this connection
563 and sets that outgoing packets may be sent to this connection as well.
564 However, this doesn't set the scheduler for outgoing traffic, it will
565 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
566 later when outgoing data is available. */
567 context = (void *)client;
568 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
570 /* Execute the protocol */
571 silc_protocol_execute(protocol, client->schedule, 0, 0);
574 /* The remote client's (the client who made the file available for download)
575 function for accepting incoming connection. This will also start the
576 key agreement protocol with the other client. */
578 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
580 SilcClientFtpSession session = (SilcClientFtpSession)context;
581 SilcClient client = session->client;
582 SilcClientConnection conn;
583 SilcSocketConnection newsocket;
584 SilcClientKEInternalContext *proto_ctx;
587 SILC_LOG_DEBUG(("Start"));
589 sock = silc_net_accept_connection(session->listener);
591 /* Call monitor callback */
592 if (session->monitor)
593 (*session->monitor)(session->client, session->conn,
594 SILC_CLIENT_FILE_MONITOR_ERROR,
595 SILC_CLIENT_FILE_ERROR, 0, 0,
596 session->client_entry, session->session_id,
597 session->filepath, session->monitor_context);
601 /* Set socket options */
602 silc_net_set_socket_nonblock(sock);
603 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
605 /* Allocate new socket connection object */
606 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
608 /* Perform name and address lookups for the remote host. */
609 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
610 if (!newsocket->hostname && !newsocket->ip) {
611 /* Call monitor callback */
612 if (session->monitor)
613 (*session->monitor)(session->client, session->conn,
614 SILC_CLIENT_FILE_MONITOR_ERROR,
615 SILC_CLIENT_FILE_ERROR, 0, 0,
616 session->client_entry, session->session_id,
617 session->filepath, session->monitor_context);
620 if (!newsocket->hostname)
621 newsocket->hostname = strdup(newsocket->ip);
622 newsocket->port = silc_net_get_remote_port(sock);
624 /* Call monitor callback */
625 if (session->monitor)
626 (*session->monitor)(session->client, session->conn,
627 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
628 SILC_CLIENT_FILE_OK, 0, 0,
629 session->client_entry, session->session_id,
630 NULL, session->monitor_context);
632 /* Add new connection for this session */
633 conn = silc_client_add_connection(client, newsocket->hostname,
634 newsocket->port, session);
635 conn->sock = newsocket;
636 conn->sock->user_data = conn;
637 session->sock = silc_socket_dup(conn->sock);
639 /* Allocate internal context for key exchange protocol. This is
640 sent as context for the protocol. */
641 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
642 proto_ctx->client = client;
643 proto_ctx->sock = silc_socket_dup(conn->sock);
644 proto_ctx->rng = client->rng;
645 proto_ctx->responder = TRUE;
646 proto_ctx->context = session;
647 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
648 proto_ctx->verify = silc_client_protocol_ke_verify_key;
650 /* Prepare the connection for key exchange protocol. We allocate the
651 protocol but will not start it yet. The connector will be the
652 initiator of the protocol thus we will wait for initiation from
653 there before we start the protocol. */
654 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
655 &newsocket->protocol, proto_ctx,
656 silc_client_ftp_key_agreement_final);
658 /* Register the connection for network input and output. This sets
659 that scheduler will listen for incoming packets for this connection
660 and sets that outgoing packets may be sent to this connection as well.
661 However, this doesn't set the scheduler for outgoing traffic, it
662 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
663 later when outgoing data is available. */
664 context = (void *)client;
665 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
668 /* Free all file transfer sessions. */
670 void silc_client_ftp_free_sessions(SilcClient client,
671 SilcClientConnection conn)
673 if (conn->ftp_sessions) {
674 SilcClientFtpSession session;
675 silc_dlist_start(conn->ftp_sessions);
676 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
678 session->sock->user_data = NULL;
679 silc_client_ftp_session_free(session);
681 silc_dlist_del(conn->ftp_sessions, session);
682 silc_dlist_uninit(conn->ftp_sessions);
686 /* Free file transfer session by client entry. */
688 void silc_client_ftp_session_free_client(SilcClientConnection conn,
689 SilcClientEntry client_entry)
691 SilcClientFtpSession session;
693 if (!conn->ftp_sessions)
696 /* Get the session */
697 silc_dlist_start(conn->ftp_sessions);
698 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
699 if (session->client_entry == client_entry) {
701 session->sock->user_data = NULL;
702 silc_client_ftp_session_free(session);
707 /* Free session resources. */
709 void silc_client_ftp_session_free(SilcClientFtpSession session)
711 SilcClientConnection conn;
713 SILC_LOG_DEBUG(("Free session"));
715 silc_dlist_del(session->conn->ftp_sessions, session);
719 silc_sftp_server_shutdown(session->sftp);
721 silc_sftp_client_shutdown(session->sftp);
725 silc_sftp_fs_memory_free(session->fs);
727 /* Destroy listener */
728 if (session->listener) {
729 silc_schedule_unset_listen_fd(session->client->schedule,
731 silc_net_close_connection(session->listener);
732 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
735 /* Destroy session connection */
737 silc_schedule_unset_listen_fd(session->client->schedule,
738 session->sock->sock);
739 silc_net_close_connection(session->sock->sock);
741 if (session->sock->user_data) {
742 conn = (SilcClientConnection)session->sock->user_data;
744 if (conn->active_session == session)
745 conn->active_session = NULL;
747 silc_client_close_connection(session->client, session->sock, conn);
749 silc_socket_free(session->sock);
754 silc_buffer_free(session->packet);
756 silc_free(session->hostname);
757 silc_free(session->filepath);
761 /* Sends a file indicated by the `filepath' to the remote client
762 indicated by the `client_entry'. This will negotiate a secret key
763 with the remote client before actually starting the transmission of
764 the file. The `monitor' callback will be called to monitor the
765 transmission of the file.
767 This returns a file session ID for the file transmission. It can
768 be used to close the session (and abort the file transmission) by
769 calling the silc_client_file_close function. The session ID is
770 also returned in the `monitor' callback. This returns 0 if the
771 file indicated by the `filepath' is being transmitted to the remote
772 client indicated by the `client_entry', already. */
774 uint32 silc_client_file_send(SilcClient client,
775 SilcClientConnection conn,
776 SilcClientFileMonitor monitor,
777 void *monitor_context,
778 const char *local_ip,
780 SilcClientEntry client_entry,
781 const char *filepath)
783 SilcClientFtpSession session;
784 SilcBuffer keyagr, ftp;
785 char *filename, *path;
787 SILC_LOG_DEBUG(("Start"));
789 /* Check for existing session for `filepath'. */
790 silc_dlist_start(conn->ftp_sessions);
791 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
792 if (!strcmp(session->filepath, filepath) &&
793 session->client_entry == client_entry)
797 /* Add new session */
798 session = silc_calloc(1, sizeof(*session));
799 session->session_id = ++conn->next_session_id;
800 session->client = client;
801 session->conn = conn;
802 session->client_entry = client_entry;
803 session->monitor = monitor;
804 session->monitor_context = monitor_context;
805 session->filepath = strdup(filepath);
806 session->server = TRUE;
807 silc_dlist_add(conn->ftp_sessions, session);
809 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
810 strcat(path, "file://");
811 strncat(path, filepath, strlen(filepath));
813 /* Allocate memory filesystem and put the file to it */
814 if (strrchr(path, '/'))
815 filename = strrchr(path, '/') + 1;
817 filename = (char *)path;
818 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
819 SILC_SFTP_FS_PERM_EXEC);
820 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
823 session->filesize = silc_file_size(filepath);
825 /* Create the listener for incoming key exchange protocol. */
827 session->hostname = strdup(local_ip);
829 session->hostname = silc_net_localip();
830 session->listener = silc_net_create_server(local_port, session->hostname);
831 if (session->listener < 0) {
832 /* Could not create listener. Do the second best thing; send empty
833 key agreement packet and let the remote client provide the point
834 for the key exchange. */
835 SILC_LOG_DEBUG(("Could not create listener"));
836 silc_free(session->hostname);
837 session->hostname = NULL;
840 session->port = silc_net_get_local_port(session->listener);
841 silc_schedule_task_add(client->schedule, session->listener,
842 silc_client_ftp_process_key_agreement, session,
843 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
846 /* Send the key agreement inside FTP packet */
847 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
849 ftp = silc_buffer_alloc(1 + keyagr->len);
850 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
851 silc_buffer_format(ftp,
853 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
855 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
856 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
857 ftp->data, ftp->len, FALSE);
859 silc_buffer_free(keyagr);
860 silc_buffer_free(ftp);
863 return session->session_id;
866 /* Receives a file from a client indicated by the `client_entry'. The
867 `session_id' indicates the file transmission session and it has been
868 received in the `ftp' client operation function. This will actually
869 perform the key agreement protocol with the remote client before
870 actually starting the file transmission. The `monitor' callback
871 will be called to monitor the transmission. */
874 silc_client_file_receive(SilcClient client,
875 SilcClientConnection conn,
876 SilcClientFileMonitor monitor,
877 void *monitor_context,
880 SilcClientFtpSession session;
881 SilcBuffer keyagr, ftp;
883 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
885 /* Get the session */
886 silc_dlist_start(conn->ftp_sessions);
887 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
888 if (session->session_id == session_id) {
893 if (session == SILC_LIST_END) {
894 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
895 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
898 /* See if we have this session running already */
899 if (session->sftp || session->listener) {
900 SILC_LOG_DEBUG(("Session already started"));
901 return SILC_CLIENT_FILE_ALREADY_STARTED;
904 session->monitor = monitor;
905 session->monitor_context = monitor_context;
906 session->conn = conn;
908 /* If the hostname and port already exists then the remote client did
909 provide the connection point to us and we won't create listener, but
910 create the connection ourselves. */
911 if (session->hostname && session->port) {
912 if (silc_client_connect_to_client(client, conn, session->port,
913 session->hostname, session) < 0)
914 return SILC_CLIENT_FILE_ERROR;
916 /* Add the listener for the key agreement */
917 session->hostname = silc_net_localip();
918 session->listener = silc_net_create_server(0, session->hostname);
919 if (session->listener < 0) {
920 SILC_LOG_DEBUG(("Could not create listener"));
921 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
922 "Cannot create listener on %s: %s",
923 session->hostname, strerror(errno));
924 return SILC_CLIENT_FILE_ERROR;
926 session->port = silc_net_get_local_port(session->listener);
927 silc_schedule_task_add(client->schedule, session->listener,
928 silc_client_ftp_process_key_agreement, session,
929 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
931 /* Send the key agreement inside FTP packet */
932 keyagr = silc_key_agreement_payload_encode(session->hostname,
934 ftp = silc_buffer_alloc(1 + keyagr->len);
935 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
936 silc_buffer_format(ftp,
938 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
940 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
941 session->client_entry->id,
942 SILC_ID_CLIENT, NULL, NULL,
943 ftp->data, ftp->len, FALSE);
945 silc_buffer_free(keyagr);
946 silc_buffer_free(ftp);
949 return SILC_CLIENT_FILE_OK;
952 /* Closes file transmission session indicated by the `session_id'.
953 If file transmission is being conducted it will be aborted
954 automatically. This function is also used to close the session
955 after successful file transmission. This function can be used
956 also to reject incoming file transmission request. */
958 SilcClientFileError silc_client_file_close(SilcClient client,
959 SilcClientConnection conn,
962 SilcClientFtpSession session;
964 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
966 /* Get the session */
967 silc_dlist_start(conn->ftp_sessions);
968 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
969 if (session->session_id == session_id) {
974 if (session == SILC_LIST_END) {
975 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
976 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
979 silc_client_ftp_session_free(session);
981 return SILC_CLIENT_FILE_OK;
984 /* Callback called after remote client information has been resolved.
985 This will try to find existing session for the client entry. If found
986 then continue with the key agreement protocol. If not then it means
987 this is a file transfer request and we let the application know. */
989 static void silc_client_ftp_resolve_cb(SilcClient client,
990 SilcClientConnection conn,
991 SilcClientEntry *clients,
992 uint32 clients_count,
995 SilcPacketContext *packet = (SilcPacketContext *)context;
996 SilcClientFtpSession session;
997 SilcKeyAgreementPayload payload = NULL;
998 SilcClientEntry client_entry;
1002 SILC_LOG_DEBUG(("Start"));
1007 client_entry = clients[0];
1009 silc_dlist_start(conn->ftp_sessions);
1010 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1011 if (session->client_entry == client_entry)
1015 /* Parse the key agreement payload */
1016 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1017 packet->buffer->len);
1021 hostname = silc_key_agreement_get_hostname(payload);
1022 port = silc_key_agreement_get_port(payload);
1024 if (session == SILC_LIST_END || (!hostname && !port)) {
1025 /* No session found, create one and let the application know about
1026 incoming file transfer request. */
1028 /* Add new session */
1029 session = silc_calloc(1, sizeof(*session));
1030 session->session_id = ++conn->next_session_id;
1031 session->client = client;
1032 session->conn = conn;
1033 session->client_entry = client_entry;
1034 silc_dlist_add(conn->ftp_sessions, session);
1036 /* Let the application know */
1037 client->ops->ftp(client, conn, client_entry,
1038 session->session_id, hostname, port);
1040 if (hostname && port) {
1041 session->hostname = strdup(hostname);
1042 session->port = port;
1048 session->hostname = strdup(hostname);
1049 session->port = port;
1051 /* Session exists, continue with key agreement protocol. */
1052 if (silc_client_connect_to_client(client, conn, port,
1053 hostname, session) < 0) {
1054 /* Call monitor callback */
1055 if (session->monitor)
1056 (*session->monitor)(session->client, session->conn,
1057 SILC_CLIENT_FILE_MONITOR_ERROR,
1058 SILC_CLIENT_FILE_ERROR, 0, 0,
1059 session->client_entry, session->session_id,
1060 session->filepath, session->monitor_context);
1065 silc_key_agreement_payload_free(payload);
1066 silc_packet_context_free(packet);
1069 /* Called when file transfer packet is received. This will parse the
1070 packet and give it to the file transfer protocol. */
1072 void silc_client_ftp(SilcClient client,
1073 SilcSocketConnection sock,
1074 SilcPacketContext *packet)
1076 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1080 SILC_LOG_DEBUG(("Start"));
1082 /* Parse the payload */
1083 ret = silc_buffer_unformat(packet->buffer,
1084 SILC_STR_UI_CHAR(&type),
1089 /* We support only type number 1 (== SFTP) */
1093 silc_buffer_pull(packet->buffer, 1);
1095 /* If we have active FTP session then give the packet directly to the
1096 protocol processor. */
1097 if (conn->active_session) {
1098 /* Give it to the SFTP */
1099 if (conn->active_session->server)
1100 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1103 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1106 /* We don't have active session, resolve the remote client information
1107 and then try to find the correct session. */
1108 SilcClientID *remote_id;
1110 if (packet->src_id_type != SILC_ID_CLIENT)
1113 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1118 /* Resolve the client */
1119 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1120 silc_client_ftp_resolve_cb,
1121 silc_packet_context_dup(packet));
1122 silc_free(remote_id);