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;
54 SilcSFTPFilesystem fs;
57 SilcSFTPHandle dir_handle;
58 SilcSFTPHandle read_handle;
60 SilcUInt64 read_offset;
64 SILC_TASK_CALLBACK(silc_client_ftp_connected)
66 SilcClientInternalConnectContext *ctx =
67 (SilcClientInternalConnectContext *)context;
68 SilcClient client = ctx->client;
69 SilcClientConnection conn = ctx->conn;
70 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
71 int opt, opt_len = sizeof(opt);
73 SILC_LOG_DEBUG(("Start"));
75 /* Check the socket status as it might be in error */
76 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
79 /* Connection failed but lets try again */
80 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
81 "Could not connect to client %s: %s",
82 ctx->host, strerror(opt));
83 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
84 "Connecting to port %d of client %s resumed",
85 ctx->port, ctx->host);
87 /* Unregister old connection try */
88 silc_schedule_unset_listen_fd(client->schedule, fd);
89 silc_net_close_connection(fd);
90 silc_schedule_task_del(client->schedule, ctx->task);
93 silc_client_connect_to_client_internal(ctx);
96 /* Connection failed and we won't try anymore */
97 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
98 "Could not connect to client %s: %s",
99 ctx->host, strerror(opt));
100 silc_schedule_unset_listen_fd(client->schedule, fd);
101 silc_net_close_connection(fd);
102 silc_schedule_task_del(client->schedule, ctx->task);
104 silc_client_ftp_session_free(session);
109 silc_schedule_unset_listen_fd(client->schedule, fd);
110 silc_schedule_task_del(client->schedule, ctx->task);
112 /* Start the key agreement */
113 silc_client_ftp_start_key_agreement(session, fd);
117 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
121 /* Create connection to server asynchronously */
122 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
126 /* Register task that will receive the async connect and will
128 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
129 silc_client_ftp_connected,
132 SILC_TASK_PRI_NORMAL);
133 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
139 silc_client_connect_to_client(SilcClient client,
140 SilcClientConnection conn, int port,
141 char *host, void *context)
143 SilcClientInternalConnectContext *ctx;
145 /* Allocate internal context for connection process. This is
146 needed as we are doing async connecting. */
147 ctx = silc_calloc(1, sizeof(*ctx));
148 ctx->client = client;
150 ctx->host = strdup(host);
153 ctx->context = context;
155 /* Do the actual connecting process */
156 return silc_client_connect_to_client_internal(ctx);
159 /* SFTP packet send callback. This will use preallocated buffer to avoid
160 reallocation of outgoing data buffer everytime. */
162 static void silc_client_ftp_send_packet(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, session->sock, SILC_PACKET_FTP, NULL,
186 0, NULL, NULL, session->packet->data,
187 session->packet->len, TRUE);
190 session->packet->data = session->packet->tail = session->packet->head;
191 session->packet->len = 0;
194 /* SFTP monitor callback for SFTP server. This reports the application
195 how the transmission is going along. This function is for the client
196 who made the file available for download. */
198 static void silc_client_ftp_monitor(SilcSFTP sftp,
199 SilcSFTPMonitors type,
200 const SilcSFTPMonitorData data,
203 SilcClientFtpSession session = (SilcClientFtpSession)context;
205 if (type == SILC_SFTP_MONITOR_READ) {
206 /* Call the monitor for application */
207 if (session->monitor)
208 (*session->monitor)(session->client, session->conn,
209 SILC_CLIENT_FILE_MONITOR_SEND,
211 data->offset, session->filesize,
212 session->client_entry, session->session_id,
213 session->filepath, session->monitor_context);
217 /* Returns the read data. This is the downloader's function (client side)
218 to receive the read data and read more until EOF is received from
219 the other side. This will also monitor the transmission and notify
222 static void silc_client_ftp_data(SilcSFTP sftp,
223 SilcSFTPStatus status,
224 const unsigned char *data,
228 SilcClientFtpSession session = (SilcClientFtpSession)context;
230 SILC_LOG_DEBUG(("Start"));
232 if (status == SILC_SFTP_STATUS_EOF) {
235 /* Close the handle */
236 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
237 session->read_handle = NULL;
239 /* Close the read file descriptor */
240 silc_file_close(session->fd);
244 if (status != SILC_SFTP_STATUS_OK) {
245 /* Call monitor callback */
246 if (session->monitor)
247 (*session->monitor)(session->client, session->conn,
248 SILC_CLIENT_FILE_MONITOR_ERROR,
249 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
250 SILC_CLIENT_FILE_NO_SUCH_FILE :
251 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
252 SILC_CLIENT_FILE_PERMISSION_DENIED :
253 SILC_CLIENT_FILE_ERROR), 0, 0,
254 session->client_entry, session->session_id,
255 session->filepath, session->monitor_context);
257 /* Close the handle */
258 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
259 session->read_handle = NULL;
261 /* Close the read file descriptor */
262 silc_file_close(session->fd);
266 /* Read more, until EOF is received */
267 session->read_offset += data_len;
268 silc_sftp_read(sftp, session->read_handle, session->read_offset,
269 SILC_PACKET_MAX_LEN - 1024,
270 silc_client_ftp_data, session);
272 /* Call monitor callback */
273 if (session->monitor)
274 (*session->monitor)(session->client, session->conn,
275 SILC_CLIENT_FILE_MONITOR_RECEIVE,
277 session->read_offset, session->filesize,
278 session->client_entry, session->session_id,
279 session->filepath, session->monitor_context);
281 /* Write the read data to the real file */
282 silc_file_write(session->fd, data, data_len);
285 /* Returns handle for the opened file. This is the downloader's function.
286 This will begin reading the data from the file. */
288 static void silc_client_ftp_open_handle(SilcSFTP sftp,
289 SilcSFTPStatus status,
290 SilcSFTPHandle handle,
293 SilcClientFtpSession session = (SilcClientFtpSession)context;
295 SILC_LOG_DEBUG(("Start"));
297 if (status != SILC_SFTP_STATUS_OK) {
298 /* Call monitor callback */
299 if (session->monitor)
300 (*session->monitor)(session->client, session->conn,
301 SILC_CLIENT_FILE_MONITOR_ERROR,
302 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
303 SILC_CLIENT_FILE_NO_SUCH_FILE :
304 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
305 SILC_CLIENT_FILE_PERMISSION_DENIED :
306 SILC_CLIENT_FILE_ERROR), 0, 0,
307 session->client_entry, session->session_id,
308 session->filepath, session->monitor_context);
312 /* Open the actual local file */
313 session->fd = silc_file_open(session->filepath,
314 O_RDWR | O_CREAT | O_EXCL);
315 if (session->fd < 0) {
316 /* Call monitor callback */
317 session->client->internal->ops->say(session->client, session->conn,
318 SILC_CLIENT_MESSAGE_ERROR,
319 "File `%s' open failed: %s",
323 if (session->monitor)
324 (*session->monitor)(session->client, session->conn,
325 SILC_CLIENT_FILE_MONITOR_ERROR,
326 SILC_CLIENT_FILE_ERROR, 0, 0,
327 session->client_entry, session->session_id,
328 session->filepath, session->monitor_context);
332 session->read_handle = handle;
334 /* Now, start reading the file */
335 silc_sftp_read(sftp, session->read_handle, session->read_offset,
336 SILC_PACKET_MAX_LEN - 1024,
337 silc_client_ftp_data, session);
339 /* Call monitor callback */
340 if (session->monitor)
341 (*session->monitor)(session->client, session->conn,
342 SILC_CLIENT_FILE_MONITOR_RECEIVE,
344 session->read_offset, session->filesize,
345 session->client_entry, session->session_id,
346 session->filepath, session->monitor_context);
349 /* Returns the file name available for download. This is the downloader's
352 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
353 SilcSFTPStatus status,
354 const SilcSFTPName name,
357 SilcClientFtpSession session = (SilcClientFtpSession)context;
358 SilcSFTPAttributesStruct attr;
360 SILC_LOG_DEBUG(("Start"));
362 if (status != SILC_SFTP_STATUS_OK) {
363 /* Call monitor callback */
364 if (session->monitor)
365 (*session->monitor)(session->client, session->conn,
366 SILC_CLIENT_FILE_MONITOR_ERROR,
367 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
368 SILC_CLIENT_FILE_NO_SUCH_FILE :
369 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
370 SILC_CLIENT_FILE_PERMISSION_DENIED :
371 SILC_CLIENT_FILE_ERROR), 0, 0,
372 session->client_entry, session->session_id,
373 session->filepath, session->monitor_context);
377 /* Now open the file */
378 memset(&attr, 0, sizeof(attr));
379 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
380 silc_client_ftp_open_handle, session);
382 /* Save the important attributes */
383 session->filepath = strdup(name->filename[0]);
384 session->filesize = name->attrs[0]->size;
386 /* Close the directory handle */
387 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
388 session->dir_handle = NULL;
391 /* Returns the file handle after giving opendir command. This is the
392 downloader's function. */
394 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
395 SilcSFTPStatus status,
396 SilcSFTPHandle handle,
399 SilcClientFtpSession session = (SilcClientFtpSession)context;
401 SILC_LOG_DEBUG(("Start"));
403 if (status != SILC_SFTP_STATUS_OK) {
404 /* Call monitor callback */
405 if (session->monitor)
406 (*session->monitor)(session->client, session->conn,
407 SILC_CLIENT_FILE_MONITOR_ERROR,
408 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
409 SILC_CLIENT_FILE_NO_SUCH_FILE :
410 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
411 SILC_CLIENT_FILE_PERMISSION_DENIED :
412 SILC_CLIENT_FILE_ERROR), 0, 0,
413 session->client_entry, session->session_id,
414 session->filepath, session->monitor_context);
418 /* Now, read the directory */
419 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
420 session->dir_handle = handle;
423 /* SFTP version callback for SFTP client. This is the downloader's function
424 after initializing the SFTP connection to the remote client. This will
425 find out the filename available for download. */
427 static void silc_client_ftp_version(SilcSFTP sftp,
428 SilcSFTPStatus status,
429 SilcSFTPVersion version,
432 SilcClientFtpSession session = (SilcClientFtpSession)context;
434 SILC_LOG_DEBUG(("Start"));
436 if (status != SILC_SFTP_STATUS_OK) {
437 /* Call monitor callback */
438 if (session->monitor)
439 (*session->monitor)(session->client, session->conn,
440 SILC_CLIENT_FILE_MONITOR_ERROR,
441 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
442 SILC_CLIENT_FILE_NO_SUCH_FILE :
443 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
444 SILC_CLIENT_FILE_PERMISSION_DENIED :
445 SILC_CLIENT_FILE_ERROR), 0, 0,
446 session->client_entry, session->session_id,
447 session->filepath, session->monitor_context);
451 /* The SFTP session is open, now retrieve the info about available file. */
452 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
455 /* This callback is called after the key agreement protocol has been
456 performed. This calls the final completion callback for the application. */
458 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
460 SilcProtocol protocol = (SilcProtocol)context;
461 SilcClientKEInternalContext *ctx =
462 (SilcClientKEInternalContext *)protocol->context;
463 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
464 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
466 SILC_LOG_DEBUG(("Start"));
468 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
469 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
470 /* Call monitor callback */
471 if (session->monitor)
472 (*session->monitor)(session->client, session->conn,
473 SILC_CLIENT_FILE_MONITOR_ERROR,
474 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
475 session->client_entry, session->session_id,
476 session->filepath, session->monitor_context);
478 /* Error occured during protocol */
479 silc_ske_free_key_material(ctx->keymat);
483 /* Set keys into use */
484 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
485 ctx->ske->prop->cipher,
486 ctx->ske->prop->pkcs,
487 ctx->ske->prop->hash,
488 ctx->ske->prop->hmac,
489 ctx->ske->prop->group,
492 if (!session->server) {
493 /* If we are the SFTP client then start the SFTP session and retrieve
494 the info about the file available for download. */
495 session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
496 session, silc_client_ftp_version,
499 /* Start SFTP server */
500 session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
501 session, session->fs);
503 /* Monitor transmission */
504 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
505 silc_client_ftp_monitor, session);
508 /* Set this as active session */
509 conn->active_session = session;
512 silc_ske_free_key_material(ctx->keymat);
514 silc_ske_free(ctx->ske);
515 silc_free(ctx->dest_id);
516 ctx->sock->protocol = NULL;
517 silc_socket_free(ctx->sock);
519 silc_protocol_free(protocol);
522 /* The downloader's function to start the key agreement protocol with the
523 remote client after we have connected to it. */
525 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
528 SilcClient client = session->client;
529 SilcClientKEInternalContext *proto_ctx;
530 SilcProtocol protocol;
531 SilcClientConnection conn;
534 SILC_LOG_DEBUG(("Start"));
536 /* Call monitor callback */
537 if (session->monitor)
538 (*session->monitor)(session->client, session->conn,
539 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
540 SILC_CLIENT_FILE_OK, 0, 0,
541 session->client_entry, session->session_id,
542 NULL, session->monitor_context);
544 /* Add new connection for this session */
545 conn = silc_client_add_connection(client, session->hostname,
546 session->port, session);
548 /* Allocate new socket connection object */
549 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
550 conn->sock->hostname = strdup(session->hostname);
551 conn->sock->port = silc_net_get_remote_port(sock);
552 session->sock = silc_socket_dup(conn->sock);
554 /* Allocate internal context for key exchange protocol. This is
555 sent as context for the protocol. */
556 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
557 proto_ctx->client = client;
558 proto_ctx->sock = silc_socket_dup(conn->sock);
559 proto_ctx->rng = client->rng;
560 proto_ctx->responder = FALSE;
561 proto_ctx->context = session;
562 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
563 proto_ctx->verify = silc_client_protocol_ke_verify_key;
565 /* Perform key exchange protocol. */
566 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
567 &protocol, (void *)proto_ctx,
568 silc_client_ftp_key_agreement_final);
569 conn->sock->protocol = protocol;
571 /* Register the connection for network input and output. This sets
572 that scheduler will listen for incoming packets for this connection
573 and sets that outgoing packets may be sent to this connection as well.
574 However, this doesn't set the scheduler for outgoing traffic, it will
575 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
576 later when outgoing data is available. */
577 context = (void *)client;
578 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
580 /* Execute the protocol */
581 silc_protocol_execute(protocol, client->schedule, 0, 0);
584 /* The remote client's (the client who made the file available for download)
585 function for accepting incoming connection. This will also start the
586 key agreement protocol with the other client. */
588 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
590 SilcClientFtpSession session = (SilcClientFtpSession)context;
591 SilcClient client = session->client;
592 SilcClientConnection conn;
593 SilcSocketConnection newsocket;
594 SilcClientKEInternalContext *proto_ctx;
597 SILC_LOG_DEBUG(("Start"));
599 sock = silc_net_accept_connection(session->listener);
601 /* Call monitor callback */
602 if (session->monitor)
603 (*session->monitor)(session->client, session->conn,
604 SILC_CLIENT_FILE_MONITOR_ERROR,
605 SILC_CLIENT_FILE_ERROR, 0, 0,
606 session->client_entry, session->session_id,
607 session->filepath, session->monitor_context);
611 /* Set socket options */
612 silc_net_set_socket_nonblock(sock);
613 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
615 /* Allocate new socket connection object */
616 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
618 /* Perform name and address lookups for the remote host. */
619 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
620 if (!newsocket->hostname && !newsocket->ip) {
621 /* Call monitor callback */
622 if (session->monitor)
623 (*session->monitor)(session->client, session->conn,
624 SILC_CLIENT_FILE_MONITOR_ERROR,
625 SILC_CLIENT_FILE_ERROR, 0, 0,
626 session->client_entry, session->session_id,
627 session->filepath, session->monitor_context);
630 if (!newsocket->hostname)
631 newsocket->hostname = strdup(newsocket->ip);
632 newsocket->port = silc_net_get_remote_port(sock);
634 /* Call monitor callback */
635 if (session->monitor)
636 (*session->monitor)(session->client, session->conn,
637 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
638 SILC_CLIENT_FILE_OK, 0, 0,
639 session->client_entry, session->session_id,
640 NULL, session->monitor_context);
642 /* Add new connection for this session */
643 conn = silc_client_add_connection(client, newsocket->hostname,
644 newsocket->port, session);
645 conn->sock = newsocket;
646 conn->sock->user_data = conn;
647 session->sock = silc_socket_dup(conn->sock);
649 /* Allocate internal context for key exchange protocol. This is
650 sent as context for the protocol. */
651 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
652 proto_ctx->client = client;
653 proto_ctx->sock = silc_socket_dup(conn->sock);
654 proto_ctx->rng = client->rng;
655 proto_ctx->responder = TRUE;
656 proto_ctx->context = session;
657 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
658 proto_ctx->verify = silc_client_protocol_ke_verify_key;
660 /* Prepare the connection for key exchange protocol. We allocate the
661 protocol but will not start it yet. The connector will be the
662 initiator of the protocol thus we will wait for initiation from
663 there before we start the protocol. */
664 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
665 &newsocket->protocol, proto_ctx,
666 silc_client_ftp_key_agreement_final);
668 /* Register the connection for network input and output. This sets
669 that scheduler will listen for incoming packets for this connection
670 and sets that outgoing packets may be sent to this connection as well.
671 However, this doesn't set the scheduler for outgoing traffic, it
672 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
673 later when outgoing data is available. */
674 context = (void *)client;
675 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
678 /* Free all file transfer sessions. */
680 void silc_client_ftp_free_sessions(SilcClient client,
681 SilcClientConnection conn)
683 if (conn->ftp_sessions) {
684 SilcClientFtpSession session;
685 silc_dlist_start(conn->ftp_sessions);
686 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
688 session->sock->user_data = NULL;
689 silc_client_ftp_session_free(session);
691 silc_dlist_del(conn->ftp_sessions, session);
692 silc_dlist_uninit(conn->ftp_sessions);
696 /* Free file transfer session by client entry. */
698 void silc_client_ftp_session_free_client(SilcClientConnection conn,
699 SilcClientEntry client_entry)
701 SilcClientFtpSession session;
703 if (!conn->ftp_sessions)
706 /* Get the session */
707 silc_dlist_start(conn->ftp_sessions);
708 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
709 if (session->client_entry == client_entry) {
711 session->sock->user_data = NULL;
712 silc_client_ftp_session_free(session);
717 /* Free session resources. */
719 void silc_client_ftp_session_free(SilcClientFtpSession session)
721 SilcClientConnection conn;
723 SILC_LOG_DEBUG(("Free session"));
725 silc_dlist_del(session->conn->ftp_sessions, session);
729 silc_sftp_server_shutdown(session->sftp);
731 silc_sftp_client_shutdown(session->sftp);
735 silc_sftp_fs_memory_free(session->fs);
737 /* Destroy listener */
738 if (session->listener) {
739 silc_schedule_unset_listen_fd(session->client->schedule,
741 silc_net_close_connection(session->listener);
742 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
745 /* Destroy session connection */
747 silc_schedule_unset_listen_fd(session->client->schedule,
748 session->sock->sock);
749 silc_net_close_connection(session->sock->sock);
751 if (session->sock->user_data) {
752 conn = (SilcClientConnection)session->sock->user_data;
754 if (conn->active_session == session)
755 conn->active_session = NULL;
757 silc_client_close_connection(session->client, session->sock, conn);
759 silc_socket_free(session->sock);
764 silc_buffer_free(session->packet);
766 silc_free(session->hostname);
767 silc_free(session->filepath);
771 /* Sends a file indicated by the `filepath' to the remote client
772 indicated by the `client_entry'. This will negotiate a secret key
773 with the remote client before actually starting the transmission of
774 the file. The `monitor' callback will be called to monitor the
775 transmission of the file. */
778 silc_client_file_send(SilcClient client,
779 SilcClientConnection conn,
780 SilcClientFileMonitor monitor,
781 void *monitor_context,
782 const char *local_ip,
783 SilcUInt32 local_port,
784 SilcClientEntry client_entry,
785 const char *filepath,
786 SilcUInt32 *session_id)
788 SilcClientFtpSession session;
789 SilcBuffer keyagr, ftp;
790 char *filename, *path;
793 SILC_LOG_DEBUG(("Start"));
795 /* Check for existing session for `filepath'. */
796 silc_dlist_start(conn->ftp_sessions);
797 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
798 if (session->filepath && !strcmp(session->filepath, filepath) &&
799 session->client_entry == client_entry)
800 return SILC_CLIENT_FILE_ALREADY_STARTED;
803 /* See whether the file exists, and can be opened in generally speaking */
804 fd = silc_file_open(filepath, O_RDONLY);
806 return SILC_CLIENT_FILE_NO_SUCH_FILE;
809 /* Add new session */
810 session = silc_calloc(1, sizeof(*session));
811 session->session_id = ++conn->next_session_id;
812 session->client = client;
813 session->conn = conn;
814 session->client_entry = client_entry;
815 session->monitor = monitor;
816 session->monitor_context = monitor_context;
817 session->filepath = strdup(filepath);
818 session->server = TRUE;
819 silc_dlist_add(conn->ftp_sessions, session);
821 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
822 strcat(path, "file://");
823 strncat(path, filepath, strlen(filepath));
825 /* Allocate memory filesystem and put the file to it */
826 if (strrchr(path, '/'))
827 filename = strrchr(path, '/') + 1;
829 filename = (char *)path;
830 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
831 SILC_SFTP_FS_PERM_EXEC);
832 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
835 session->filesize = silc_file_size(filepath);
837 /* Create the listener for incoming key exchange protocol. */
839 session->hostname = strdup(local_ip);
841 session->hostname = silc_net_localip();
842 session->listener = silc_net_create_server(local_port, session->hostname);
843 if (session->listener < 0) {
844 /* Could not create listener. Do the second best thing; send empty
845 key agreement packet and let the remote client provide the point
846 for the key exchange. */
847 SILC_LOG_DEBUG(("Could not create listener"));
848 silc_free(session->hostname);
849 session->hostname = NULL;
853 session->port = silc_net_get_local_port(session->listener);
854 silc_schedule_task_add(client->schedule, session->listener,
855 silc_client_ftp_process_key_agreement, session,
856 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
859 /* Send the key agreement inside FTP packet */
860 keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
862 ftp = silc_buffer_alloc(1 + keyagr->len);
863 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
864 silc_buffer_format(ftp,
866 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
868 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
869 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
870 ftp->data, ftp->len, FALSE);
872 silc_buffer_free(keyagr);
873 silc_buffer_free(ftp);
877 *session_id = session->session_id;
879 return SILC_CLIENT_FILE_OK;
882 /* Receives a file from a client indicated by the `client_entry'. The
883 `session_id' indicates the file transmission session and it has been
884 received in the `ftp' client operation function. This will actually
885 perform the key agreement protocol with the remote client before
886 actually starting the file transmission. The `monitor' callback
887 will be called to monitor the transmission. */
890 silc_client_file_receive(SilcClient client,
891 SilcClientConnection conn,
892 SilcClientFileMonitor monitor,
893 void *monitor_context,
894 SilcUInt32 session_id)
896 SilcClientFtpSession session;
897 SilcBuffer keyagr, ftp;
899 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
901 /* Get the session */
902 silc_dlist_start(conn->ftp_sessions);
903 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
904 if (session->session_id == session_id) {
909 if (session == SILC_LIST_END) {
910 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
911 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
914 /* See if we have this session running already */
915 if (session->sftp || session->listener) {
916 SILC_LOG_DEBUG(("Session already started"));
917 return SILC_CLIENT_FILE_ALREADY_STARTED;
920 session->monitor = monitor;
921 session->monitor_context = monitor_context;
922 session->conn = conn;
924 /* If the hostname and port already exists then the remote client did
925 provide the connection point to us and we won't create listener, but
926 create the connection ourselves. */
927 if (session->hostname && session->port) {
928 if (silc_client_connect_to_client(client, conn, session->port,
929 session->hostname, session) < 0)
930 return SILC_CLIENT_FILE_ERROR;
932 /* Add the listener for the key agreement */
933 session->hostname = silc_net_localip();
934 session->listener = silc_net_create_server(0, session->hostname);
935 if (session->listener < 0) {
936 SILC_LOG_DEBUG(("Could not create listener"));
937 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
938 "Cannot create listener on %s: %s",
939 session->hostname, strerror(errno));
940 return SILC_CLIENT_FILE_ERROR;
942 session->port = silc_net_get_local_port(session->listener);
943 silc_schedule_task_add(client->schedule, session->listener,
944 silc_client_ftp_process_key_agreement, session,
945 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
947 /* Send the key agreement inside FTP packet */
948 keyagr = silc_key_agreement_payload_encode(session->hostname,
950 ftp = silc_buffer_alloc(1 + keyagr->len);
951 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
952 silc_buffer_format(ftp,
954 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
956 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
957 session->client_entry->id,
958 SILC_ID_CLIENT, NULL, NULL,
959 ftp->data, ftp->len, FALSE);
961 silc_buffer_free(keyagr);
962 silc_buffer_free(ftp);
965 return SILC_CLIENT_FILE_OK;
968 /* Closes file transmission session indicated by the `session_id'.
969 If file transmission is being conducted it will be aborted
970 automatically. This function is also used to close the session
971 after successful file transmission. This function can be used
972 also to reject incoming file transmission request. */
974 SilcClientFileError silc_client_file_close(SilcClient client,
975 SilcClientConnection conn,
976 SilcUInt32 session_id)
978 SilcClientFtpSession session;
980 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
982 /* Get the session */
983 silc_dlist_start(conn->ftp_sessions);
984 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
985 if (session->session_id == session_id) {
990 if (session == SILC_LIST_END) {
991 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
992 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
995 silc_client_ftp_session_free(session);
997 return SILC_CLIENT_FILE_OK;
1000 /* Callback called after remote client information has been resolved.
1001 This will try to find existing session for the client entry. If found
1002 then continue with the key agreement protocol. If not then it means
1003 this is a file transfer request and we let the application know. */
1005 static void silc_client_ftp_resolve_cb(SilcClient client,
1006 SilcClientConnection conn,
1007 SilcClientEntry *clients,
1008 SilcUInt32 clients_count,
1011 SilcPacketContext *packet = (SilcPacketContext *)context;
1012 SilcClientFtpSession session;
1013 SilcKeyAgreementPayload payload = NULL;
1014 SilcClientEntry client_entry;
1018 SILC_LOG_DEBUG(("Start"));
1023 client_entry = clients[0];
1025 silc_dlist_start(conn->ftp_sessions);
1026 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1027 if (session->client_entry == client_entry)
1031 /* Parse the key agreement payload */
1032 payload = silc_key_agreement_payload_parse(packet->buffer->data,
1033 packet->buffer->len);
1037 hostname = silc_key_agreement_get_hostname(payload);
1038 port = silc_key_agreement_get_port(payload);
1044 if (session == SILC_LIST_END || (!hostname && !port)) {
1045 /* No session found, create one and let the application know about
1046 incoming file transfer request. */
1048 /* Add new session */
1049 session = silc_calloc(1, sizeof(*session));
1050 session->session_id = ++conn->next_session_id;
1051 session->client = client;
1052 session->conn = conn;
1053 session->client_entry = client_entry;
1054 silc_dlist_add(conn->ftp_sessions, session);
1056 /* Let the application know */
1057 client->internal->ops->ftp(client, conn, client_entry,
1058 session->session_id, hostname, port);
1060 if (hostname && port) {
1061 session->hostname = strdup(hostname);
1062 session->port = port;
1068 session->hostname = strdup(hostname);
1069 session->port = port;
1071 /* Session exists, continue with key agreement protocol. */
1072 if (silc_client_connect_to_client(client, conn, port,
1073 hostname, session) < 0) {
1074 /* Call monitor callback */
1075 if (session->monitor)
1076 (*session->monitor)(session->client, session->conn,
1077 SILC_CLIENT_FILE_MONITOR_ERROR,
1078 SILC_CLIENT_FILE_ERROR, 0, 0,
1079 session->client_entry, session->session_id,
1080 session->filepath, session->monitor_context);
1085 silc_key_agreement_payload_free(payload);
1086 silc_packet_context_free(packet);
1089 /* Called when file transfer packet is received. This will parse the
1090 packet and give it to the file transfer protocol. */
1092 void silc_client_ftp(SilcClient client,
1093 SilcSocketConnection sock,
1094 SilcPacketContext *packet)
1096 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1100 SILC_LOG_DEBUG(("Start"));
1102 /* Parse the payload */
1103 ret = silc_buffer_unformat(packet->buffer,
1104 SILC_STR_UI_CHAR(&type),
1109 /* We support only type number 1 (== SFTP) */
1113 silc_buffer_pull(packet->buffer, 1);
1115 /* If we have active FTP session then give the packet directly to the
1116 protocol processor. */
1117 if (conn->active_session) {
1118 /* Give it to the SFTP */
1119 if (conn->active_session->server)
1120 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1123 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1126 /* We don't have active session, resolve the remote client information
1127 and then try to find the correct session. */
1128 SilcClientID *remote_id;
1130 if (packet->src_id_type != SILC_ID_CLIENT)
1133 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1138 /* Resolve the client */
1139 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1140 silc_client_ftp_resolve_cb,
1141 silc_packet_context_dup(packet));
1142 silc_free(remote_id);