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