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 we are the SFTP client then start the SFTP session and retrieve
481 the info about the file available for download. */
482 if (!session->server) {
483 session->sftp = silc_sftp_client_start(conn->sock,
484 silc_client_ftp_send_packet,
486 silc_client_ftp_version, session);
489 /* Set this as active session */
490 conn->active_session = session;
493 silc_ske_free_key_material(ctx->keymat);
495 silc_ske_free(ctx->ske);
496 silc_free(ctx->dest_id);
497 ctx->sock->protocol = NULL;
498 silc_socket_free(ctx->sock);
500 silc_protocol_free(protocol);
503 /* The downloader's function to start the key agreement protocol with the
504 remote client after we have connected to it. */
506 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
509 SilcClient client = session->client;
510 SilcClientKEInternalContext *proto_ctx;
511 SilcProtocol protocol;
512 SilcClientConnection conn;
515 SILC_LOG_DEBUG(("Start"));
517 /* Call monitor callback */
518 if (session->monitor)
519 (*session->monitor)(session->client, session->conn,
520 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
521 SILC_CLIENT_FILE_OK, 0, 0,
522 session->client_entry, session->session_id,
523 NULL, session->monitor_context);
525 /* Add new connection for this session */
526 conn = silc_client_add_connection(client, session->hostname,
527 session->port, session);
529 /* Allocate new socket connection object */
530 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
531 conn->sock->hostname = strdup(session->hostname);
532 conn->sock->port = silc_net_get_remote_port(sock);
533 session->sock = silc_socket_dup(conn->sock);
535 /* Allocate the SFTP */
536 if (session->server) {
537 session->sftp = silc_sftp_server_start(conn->sock,
538 silc_client_ftp_send_packet,
539 session, session->fs);
541 /* Monitor transmission */
542 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
543 silc_client_ftp_monitor, session);
546 /* Allocate internal context for key exchange protocol. This is
547 sent as context for the protocol. */
548 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
549 proto_ctx->client = client;
550 proto_ctx->sock = silc_socket_dup(conn->sock);
551 proto_ctx->rng = client->rng;
552 proto_ctx->responder = FALSE;
553 proto_ctx->context = session;
554 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
555 proto_ctx->verify = silc_client_protocol_ke_verify_key;
557 /* Perform key exchange protocol. */
558 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
559 &protocol, (void *)proto_ctx,
560 silc_client_ftp_key_agreement_final);
561 conn->sock->protocol = protocol;
563 /* Register the connection for network input and output. This sets
564 that scheduler will listen for incoming packets for this connection
565 and sets that outgoing packets may be sent to this connection as well.
566 However, this doesn't set the scheduler for outgoing traffic, it will
567 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
568 later when outgoing data is available. */
569 context = (void *)client;
570 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
572 /* Execute the protocol */
573 silc_protocol_execute(protocol, client->schedule, 0, 0);
576 /* The remote client's (the client who made the file available for download)
577 function for accepting incoming connection. This will also start the
578 key agreement protocol with the other client. */
580 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
582 SilcClientFtpSession session = (SilcClientFtpSession)context;
583 SilcClient client = session->client;
584 SilcClientConnection conn;
585 SilcSocketConnection newsocket;
586 SilcClientKEInternalContext *proto_ctx;
589 SILC_LOG_DEBUG(("Start"));
591 sock = silc_net_accept_connection(session->listener);
593 /* Call monitor callback */
594 if (session->monitor)
595 (*session->monitor)(session->client, session->conn,
596 SILC_CLIENT_FILE_MONITOR_ERROR,
597 SILC_CLIENT_FILE_ERROR, 0, 0,
598 session->client_entry, session->session_id,
599 session->filepath, session->monitor_context);
603 /* Set socket options */
604 silc_net_set_socket_nonblock(sock);
605 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
607 /* Allocate new socket connection object */
608 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
610 /* Perform name and address lookups for the remote host. */
611 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
612 if (!newsocket->hostname && !newsocket->ip) {
613 /* Call monitor callback */
614 if (session->monitor)
615 (*session->monitor)(session->client, session->conn,
616 SILC_CLIENT_FILE_MONITOR_ERROR,
617 SILC_CLIENT_FILE_ERROR, 0, 0,
618 session->client_entry, session->session_id,
619 session->filepath, session->monitor_context);
622 if (!newsocket->hostname)
623 newsocket->hostname = strdup(newsocket->ip);
624 newsocket->port = silc_net_get_remote_port(sock);
626 /* Call monitor callback */
627 if (session->monitor)
628 (*session->monitor)(session->client, session->conn,
629 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,
630 SILC_CLIENT_FILE_OK, 0, 0,
631 session->client_entry, session->session_id,
632 NULL, session->monitor_context);
634 /* Add new connection for this session */
635 conn = silc_client_add_connection(client, newsocket->hostname,
636 newsocket->port, session);
637 conn->sock = newsocket;
638 conn->sock->user_data = conn;
639 session->sock = silc_socket_dup(conn->sock);
641 /* Allocate internal context for key exchange protocol. This is
642 sent as context for the protocol. */
643 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
644 proto_ctx->client = client;
645 proto_ctx->sock = silc_socket_dup(conn->sock);
646 proto_ctx->rng = client->rng;
647 proto_ctx->responder = TRUE;
648 proto_ctx->context = session;
649 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
650 proto_ctx->verify = silc_client_protocol_ke_verify_key;
652 /* Prepare the connection for key exchange protocol. We allocate the
653 protocol but will not start it yet. The connector will be the
654 initiator of the protocol thus we will wait for initiation from
655 there before we start the protocol. */
656 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
657 &newsocket->protocol, proto_ctx,
658 silc_client_ftp_key_agreement_final);
660 /* Register the connection for network input and output. This sets
661 that scheduler will listen for incoming packets for this connection
662 and sets that outgoing packets may be sent to this connection as well.
663 However, this doesn't set the scheduler for outgoing traffic, it
664 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
665 later when outgoing data is available. */
666 context = (void *)client;
667 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
670 /* Free all file transfer sessions. */
672 void silc_client_ftp_free_sessions(SilcClient client,
673 SilcClientConnection conn)
675 if (conn->ftp_sessions) {
676 SilcClientFtpSession session;
677 silc_dlist_start(conn->ftp_sessions);
678 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
680 session->sock->user_data = NULL;
681 silc_client_ftp_session_free(session);
683 silc_dlist_del(conn->ftp_sessions, session);
684 silc_dlist_uninit(conn->ftp_sessions);
688 /* Free file transfer session by client entry. */
690 void silc_client_ftp_session_free_client(SilcClientConnection conn,
691 SilcClientEntry client_entry)
693 SilcClientFtpSession session;
695 if (!conn->ftp_sessions)
698 /* Get the session */
699 silc_dlist_start(conn->ftp_sessions);
700 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
701 if (session->client_entry == client_entry) {
703 session->sock->user_data = NULL;
704 silc_client_ftp_session_free(session);
709 /* Free session resources. */
711 void silc_client_ftp_session_free(SilcClientFtpSession session)
713 SilcClientConnection conn;
715 SILC_LOG_DEBUG(("Free session"));
717 silc_dlist_del(session->conn->ftp_sessions, session);
721 silc_sftp_server_shutdown(session->sftp);
723 silc_sftp_client_shutdown(session->sftp);
727 silc_sftp_fs_memory_free(session->fs);
729 /* Destroy listener */
730 if (session->listener) {
731 silc_schedule_unset_listen_fd(session->client->schedule,
733 silc_net_close_connection(session->listener);
734 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
737 /* Destroy session connection */
739 silc_schedule_unset_listen_fd(session->client->schedule,
740 session->sock->sock);
741 silc_net_close_connection(session->sock->sock);
743 if (session->sock->user_data) {
744 conn = (SilcClientConnection)session->sock->user_data;
746 if (conn->active_session == session)
747 conn->active_session = NULL;
749 silc_client_close_connection(session->client, session->sock, conn);
751 silc_socket_free(session->sock);
756 silc_buffer_free(session->packet);
758 silc_free(session->hostname);
759 silc_free(session->filepath);
763 /* Sends a file indicated by the `filepath' to the remote client
764 indicated by the `client_entry'. This will negotiate a secret key
765 with the remote client before actually starting the transmission of
766 the file. The `monitor' callback will be called to monitor the
767 transmission of the file.
769 This returns a file session ID for the file transmission. It can
770 be used to close the session (and abort the file transmission) by
771 calling the silc_client_file_close function. The session ID is
772 also returned in the `monitor' callback. This returns 0 if the
773 file indicated by the `filepath' is being transmitted to the remote
774 client indicated by the `client_entry', already. */
776 uint32 silc_client_file_send(SilcClient client,
777 SilcClientConnection conn,
778 SilcClientFileMonitor monitor,
779 void *monitor_context,
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 /* Send the key agreement inside FTP packet */
826 keyagr = silc_key_agreement_payload_encode(NULL, 0);
828 ftp = silc_buffer_alloc(1 + keyagr->len);
829 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
830 silc_buffer_format(ftp,
832 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
834 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
835 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
836 ftp->data, ftp->len, FALSE);
838 silc_buffer_free(keyagr);
839 silc_buffer_free(ftp);
842 return session->session_id;
845 /* Receives a file from a client indicated by the `client_entry'. The
846 `session_id' indicates the file transmission session and it has been
847 received in the `ftp' client operation function. This will actually
848 perform the key agreement protocol with the remote client before
849 actually starting the file transmission. The `monitor' callback
850 will be called to monitor the transmission. */
853 silc_client_file_receive(SilcClient client,
854 SilcClientConnection conn,
855 SilcClientFileMonitor monitor,
856 void *monitor_context,
859 SilcClientFtpSession session;
860 SilcBuffer keyagr, ftp;
862 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
864 /* Get the session */
865 silc_dlist_start(conn->ftp_sessions);
866 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
867 if (session->session_id == session_id) {
872 if (session == SILC_LIST_END) {
873 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
874 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
877 /* See if we have this session running already */
878 if (session->sftp || session->listener) {
879 SILC_LOG_DEBUG(("Session already started"));
880 return SILC_CLIENT_FILE_ALREADY_STARTED;
883 session->monitor = monitor;
884 session->monitor_context = monitor_context;
885 session->conn = conn;
887 /* If the hostname and port already exists then the remote client did
888 provide the connection point to us and we won't create listener, but
889 create the connection ourselves. */
890 if (session->hostname && session->port) {
891 if (silc_client_connect_to_client(client, conn, session->port,
892 session->hostname, session) < 0)
893 return SILC_CLIENT_FILE_ERROR;
895 /* Add the listener for the key agreement */
896 session->hostname = silc_net_localip();
897 session->listener = silc_net_create_server(0, session->hostname);
898 if (session->listener < 0) {
899 SILC_LOG_DEBUG(("Could not create listener"));
900 return SILC_CLIENT_FILE_ERROR;
902 session->port = silc_net_get_local_port(session->listener);
903 silc_schedule_task_add(client->schedule, session->listener,
904 silc_client_ftp_process_key_agreement, session,
905 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
907 /* Send the key agreement inside FTP packet */
908 keyagr = silc_key_agreement_payload_encode(session->hostname,
910 ftp = silc_buffer_alloc(1 + keyagr->len);
911 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
912 silc_buffer_format(ftp,
914 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
916 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
917 session->client_entry->id,
918 SILC_ID_CLIENT, NULL, NULL,
919 ftp->data, ftp->len, FALSE);
921 silc_buffer_free(keyagr);
922 silc_buffer_free(ftp);
925 return SILC_CLIENT_FILE_OK;
928 /* Closes file transmission session indicated by the `session_id'.
929 If file transmission is being conducted it will be aborted
930 automatically. This function is also used to close the session
931 after successful file transmission. This function can be used
932 also to reject incoming file transmission request. */
934 SilcClientFileError silc_client_file_close(SilcClient client,
935 SilcClientConnection conn,
938 SilcClientFtpSession session;
940 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
942 /* Get the session */
943 silc_dlist_start(conn->ftp_sessions);
944 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
945 if (session->session_id == session_id) {
950 if (session == SILC_LIST_END) {
951 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
952 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
955 silc_client_ftp_session_free(session);
957 return SILC_CLIENT_FILE_OK;
960 /* Callback called after remote client information has been resolved.
961 This will try to find existing session for the client entry. If found
962 then continue with the key agreement protocol. If not then it means
963 this is a file transfer request and we let the application know. */
965 static void silc_client_ftp_resolve_cb(SilcClient client,
966 SilcClientConnection conn,
967 SilcClientEntry *clients,
968 uint32 clients_count,
971 SilcPacketContext *packet = (SilcPacketContext *)context;
972 SilcClientFtpSession session;
973 SilcKeyAgreementPayload payload = NULL;
974 SilcClientEntry client_entry;
978 SILC_LOG_DEBUG(("Start"));
983 client_entry = clients[0];
985 silc_dlist_start(conn->ftp_sessions);
986 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
987 if (session->client_entry == client_entry)
991 /* Parse the key agreement payload */
992 payload = silc_key_agreement_payload_parse(packet->buffer);
996 hostname = silc_key_agreement_get_hostname(payload);
997 port = silc_key_agreement_get_port(payload);
999 if (session == SILC_LIST_END || (!hostname && !port)) {
1000 /* No session found, create one and let the application know about
1001 incoming file transfer request. */
1003 /* Add new session */
1004 session = silc_calloc(1, sizeof(*session));
1005 session->session_id = ++conn->next_session_id;
1006 session->client = client;
1007 session->conn = conn;
1008 session->client_entry = client_entry;
1009 silc_dlist_add(conn->ftp_sessions, session);
1011 /* Let the application know */
1012 client->ops->ftp(client, conn, client_entry,
1013 session->session_id, hostname, port);
1015 if (hostname && port) {
1016 session->hostname = strdup(hostname);
1017 session->port = port;
1023 session->hostname = strdup(hostname);
1024 session->port = port;
1026 /* Session exists, continue with key agreement protocol. */
1027 if (silc_client_connect_to_client(client, conn, port,
1028 hostname, session) < 0) {
1029 /* Call monitor callback */
1030 if (session->monitor)
1031 (*session->monitor)(session->client, session->conn,
1032 SILC_CLIENT_FILE_MONITOR_ERROR,
1033 SILC_CLIENT_FILE_ERROR, 0, 0,
1034 session->client_entry, session->session_id,
1035 session->filepath, session->monitor_context);
1040 silc_key_agreement_payload_free(payload);
1041 silc_packet_context_free(packet);
1044 /* Called when file transfer packet is received. This will parse the
1045 packet and give it to the file transfer protocol. */
1047 void silc_client_ftp(SilcClient client,
1048 SilcSocketConnection sock,
1049 SilcPacketContext *packet)
1051 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1055 SILC_LOG_DEBUG(("Start"));
1057 /* Parse the payload */
1058 ret = silc_buffer_unformat(packet->buffer,
1059 SILC_STR_UI_CHAR(&type),
1064 /* We support only type number 1 (== SFTP) */
1068 silc_buffer_pull(packet->buffer, 1);
1070 /* If we have active FTP session then give the packet directly to the
1071 protocol processor. */
1072 if (conn->active_session) {
1073 /* Give it to the SFTP */
1074 if (conn->active_session->server)
1075 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1078 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1081 /* We don't have active session, resolve the remote client information
1082 and then try to find the correct session. */
1083 SilcClientID *remote_id;
1085 if (packet->src_id_type != SILC_ID_CLIENT)
1088 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1093 /* Resolve the client */
1094 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1095 silc_client_ftp_resolve_cb,
1096 silc_packet_context_dup(packet));
1097 silc_free(remote_id);