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,
33 static void silc_client_ftp_session_free(SilcClientFtpSession session);
35 /* File transmission session */
36 struct SilcClientFtpSessionStruct {
39 SilcClientConnection conn;
40 SilcClientEntry client_entry;
46 SilcClientFileMonitor monitor;
47 void *monitor_context;
51 SilcSFTPFilesystem fs;
54 SilcSFTPHandle dir_handle;
55 SilcSFTPHandle read_handle;
61 /* SFTP packet send callback */
63 static void silc_client_ftp_send_packet(SilcSocketConnection sock,
64 SilcBuffer packet, void *context)
66 SilcClientFtpSession session = (SilcClientFtpSession)context;
67 SilcClient client = session->client;
69 SILC_LOG_DEBUG(("Start"));
71 /* Send the packet immediately */
72 silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
73 packet->data, packet->len, TRUE);
76 /* Returns the read data */
78 static void silc_client_ftp_data(SilcSFTP sftp,
79 SilcSFTPStatus status,
80 const unsigned char *data,
84 SilcClientFtpSession session = (SilcClientFtpSession)context;
86 SILC_LOG_DEBUG(("Start"));
88 if (status == SILC_SFTP_STATUS_EOF) {
90 /* Close the handle */
91 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
92 session->read_handle = NULL;
94 /* Close the read file descriptor */
95 silc_file_close(session->fd);
99 if (status != SILC_SFTP_STATUS_OK) {
102 /* Close the handle */
103 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
104 session->read_handle = NULL;
106 /* Close the read file descriptor */
107 silc_file_close(session->fd);
111 /* Read more, until EOF is received */
112 session->read_offset += data_len;
113 silc_sftp_read(sftp, session->read_handle, session->read_offset, 16384,
114 silc_client_ftp_data, session);
116 /* Call monitor callback */
117 if (session->monitor)
118 (*session->monitor)(session->client, session->conn,
119 SILC_CLIENT_FILE_MONITOR_RECEIVE,
120 session->read_offset, session->filesize,
121 session->client_entry, session->session_id,
122 session->filepath, session->monitor_context);
124 /* Write the read data */
125 silc_file_write(session->fd, data, data_len);
128 static void silc_client_ftp_open_handle(SilcSFTP sftp,
129 SilcSFTPStatus status,
130 SilcSFTPHandle handle,
133 SilcClientFtpSession session = (SilcClientFtpSession)context;
135 SILC_LOG_DEBUG(("Start"));
137 if (status != SILC_SFTP_STATUS_OK) {
141 /* Open the actual local file */
142 session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT);
143 if (session->fd < 0) {
147 /* Now, start reading the file */
148 silc_sftp_read(sftp, handle, session->read_offset, 16384,
149 silc_client_ftp_data, session);
151 /* Call monitor callback */
152 if (session->monitor)
153 (*session->monitor)(session->client, session->conn,
154 SILC_CLIENT_FILE_MONITOR_RECEIVE,
155 session->read_offset, session->filesize,
156 session->client_entry, session->session_id,
157 session->filepath, session->monitor_context);
160 /* Returns the file name available for download. */
162 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
163 SilcSFTPStatus status,
164 const SilcSFTPName name,
167 SilcClientFtpSession session = (SilcClientFtpSession)context;
168 SilcSFTPAttributesStruct attr;
170 SILC_LOG_DEBUG(("Start"));
172 if (status != SILC_SFTP_STATUS_OK) {
176 /* Now open the file */
177 memset(&attr, 0, sizeof(attr));
178 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
179 silc_client_ftp_open_handle, session);
181 /* Save the important attributes */
182 session->filepath = strdup(name->filename[0]);
183 session->filesize = name->attrs[0]->size;
185 /* Close the directory handle */
186 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
187 session->dir_handle = NULL;
190 /* Returns the file handle after giving opendir command. */
192 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
193 SilcSFTPStatus status,
194 SilcSFTPHandle handle,
197 SilcClientFtpSession session = (SilcClientFtpSession)context;
199 SILC_LOG_DEBUG(("Start"));
201 if (status != SILC_SFTP_STATUS_OK) {
205 /* Now, read the directory */
206 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
207 session->dir_handle = handle;
210 /* SFTP version callback for SFTP client */
212 static void silc_client_ftp_version(SilcSFTP sftp,
213 SilcSFTPStatus status,
214 SilcSFTPVersion version,
217 SilcClientFtpSession session = (SilcClientFtpSession)context;
219 SILC_LOG_DEBUG(("Start"));
221 if (status != SILC_SFTP_STATUS_OK) {
225 /* The SFTP session is open, now retrieve the info about available file. */
226 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
229 /* This callback is called after the key agreement protocol has been
230 performed. This calls the final completion callback for the application. */
232 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
234 SilcProtocol protocol = (SilcProtocol)context;
235 SilcClientKEInternalContext *ctx =
236 (SilcClientKEInternalContext *)protocol->context;
237 SilcClient client = (SilcClient)ctx->client;
238 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
239 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
241 SILC_LOG_DEBUG(("Start"));
243 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
244 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
245 /* Error occured during protocol */
246 silc_ske_free_key_material(ctx->keymat);
250 /* Set keys into use */
251 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
252 ctx->ske->prop->cipher,
253 ctx->ske->prop->pkcs,
254 ctx->ske->prop->hash,
255 ctx->ske->prop->hmac,
256 ctx->ske->prop->group);
258 /* If we are the SFTP client then start the SFTP session and retrieve
259 the info about the file available for download. */
260 if (!session->server) {
261 session->sftp = silc_sftp_client_start(conn->sock,
262 silc_client_ftp_send_packet,
264 silc_client_ftp_version, session);
268 silc_ske_free_key_material(ctx->keymat);
270 silc_ske_free(ctx->ske);
271 silc_free(ctx->dest_id);
272 silc_socket_free(ctx->sock);
274 ctx->sock->protocol = NULL;
275 silc_protocol_free(protocol);
278 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
281 SilcClient client = session->client;
282 SilcClientKEInternalContext *proto_ctx;
283 SilcProtocol protocol;
284 SilcClientConnection conn;
287 SILC_LOG_DEBUG(("Start"));
289 /* Add new connection for this session */
290 conn = silc_client_add_connection(client, session->hostname,
291 session->port, session);
293 /* Allocate new socket connection object */
294 silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &conn->sock);
295 conn->sock->hostname = strdup(session->hostname);
296 conn->sock->port = silc_net_get_remote_port(sock);
298 /* Allocate the SFTP */
300 session->sftp = silc_sftp_server_start(conn->sock,
301 silc_client_ftp_send_packet,
302 session, session->fs);
304 /* Allocate internal context for key exchange protocol. This is
305 sent as context for the protocol. */
306 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
307 proto_ctx->client = client;
308 proto_ctx->sock = silc_socket_dup(conn->sock);
309 proto_ctx->rng = client->rng;
310 proto_ctx->responder = FALSE;
311 proto_ctx->context = session;
312 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
313 proto_ctx->verify = silc_client_protocol_ke_verify_key;
315 /* Perform key exchange protocol. */
316 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
317 &protocol, (void *)proto_ctx,
318 silc_client_ftp_key_agreement_final);
320 /* Register the connection for network input and output. This sets
321 that scheduler will listen for incoming packets for this connection
322 and sets that outgoing packets may be sent to this connection as well.
323 However, this doesn't set the scheduler for outgoing traffic, it will
324 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
325 later when outgoing data is available. */
326 context = (void *)client;
327 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
329 /* Execute the protocol */
330 silc_protocol_execute(protocol, client->schedule, 0, 0);
333 SILC_TASK_CALLBACK(silc_client_ftp_connected)
335 SilcClientInternalConnectContext *ctx =
336 (SilcClientInternalConnectContext *)context;
337 SilcClient client = ctx->client;
338 SilcClientConnection conn = ctx->conn;
339 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
340 int opt, opt_len = sizeof(opt);
342 SILC_LOG_DEBUG(("Start"));
344 /* Check the socket status as it might be in error */
345 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
347 if (ctx->tries < 2) {
348 /* Connection failed but lets try again */
349 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
350 "Could not connect to client %s: %s",
351 ctx->host, strerror(opt));
352 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
353 "Connecting to port %d of client %s resumed",
354 ctx->port, ctx->host);
356 /* Unregister old connection try */
357 silc_schedule_unset_listen_fd(client->schedule, fd);
358 silc_net_close_connection(fd);
359 silc_schedule_task_del(client->schedule, ctx->task);
362 silc_client_connect_to_client_internal(ctx);
365 /* Connection failed and we won't try anymore */
366 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
367 "Could not connect to client %s: %s",
368 ctx->host, strerror(opt));
369 silc_schedule_unset_listen_fd(client->schedule, fd);
370 silc_net_close_connection(fd);
371 silc_schedule_task_del(client->schedule, ctx->task);
373 silc_client_ftp_session_free(session);
378 silc_schedule_unset_listen_fd(client->schedule, fd);
379 silc_schedule_task_del(client->schedule, ctx->task);
381 /* Start the key agreement */
382 silc_client_ftp_start_key_agreement(session, fd);
386 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
390 /* Create connection to server asynchronously */
391 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
395 /* Register task that will receive the async connect and will
397 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
398 silc_client_ftp_connected,
401 SILC_TASK_PRI_NORMAL);
402 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
410 silc_client_connect_to_client(SilcClient client,
411 SilcClientConnection conn, int port,
412 char *host, void *context)
414 SilcClientInternalConnectContext *ctx;
416 /* Allocate internal context for connection process. This is
417 needed as we are doing async connecting. */
418 ctx = silc_calloc(1, sizeof(*ctx));
419 ctx->client = client;
421 ctx->host = strdup(host);
424 ctx->context = context;
426 /* Do the actual connecting process */
427 return silc_client_connect_to_client_internal(ctx);
432 static void silc_client_ftp_session_free(SilcClientFtpSession session)
434 silc_dlist_del(session->conn->ftp_sessions, session);
438 silc_sftp_server_shutdown(session->sftp);
440 silc_sftp_client_shutdown(session->sftp);
444 silc_sftp_fs_memory_free(session->fs);
446 silc_free(session->hostname);
447 silc_free(session->filepath);
451 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
453 SilcClientFtpSession session = (SilcClientFtpSession)context;
454 SilcClient client = session->client;
455 SilcClientConnection conn;
456 SilcSocketConnection newsocket;
457 SilcClientKEInternalContext *proto_ctx;
460 SILC_LOG_DEBUG(("Start"));
462 sock = silc_net_accept_connection(session->listener);
468 /* Set socket options */
469 silc_net_set_socket_nonblock(sock);
470 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
472 /* Allocate new socket connection object */
473 silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
475 /* Perform name and address lookups for the remote host. */
476 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
477 if (!newsocket->hostname && !newsocket->ip) {
481 if (!newsocket->hostname)
482 newsocket->hostname = strdup(newsocket->ip);
483 newsocket->port = silc_net_get_remote_port(sock);
485 /* Add new connection for this session */
486 conn = silc_client_add_connection(client, newsocket->hostname,
487 newsocket->port, session);
488 conn->sock = newsocket;
489 conn->sock->user_data = conn;
491 /* Allocate internal context for key exchange protocol. This is
492 sent as context for the protocol. */
493 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
494 proto_ctx->client = client;
495 proto_ctx->sock = silc_socket_dup(conn->sock);
496 proto_ctx->rng = client->rng;
497 proto_ctx->responder = TRUE;
498 proto_ctx->context = session;
499 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
500 proto_ctx->verify = silc_client_protocol_ke_verify_key;
502 /* Prepare the connection for key exchange protocol. We allocate the
503 protocol but will not start it yet. The connector will be the
504 initiator of the protocol thus we will wait for initiation from
505 there before we start the protocol. */
506 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
507 &newsocket->protocol, proto_ctx,
508 silc_client_ftp_key_agreement_final);
510 /* Register the connection for network input and output. This sets
511 that scheduler will listen for incoming packets for this connection
512 and sets that outgoing packets may be sent to this connection as well.
513 However, this doesn't set the scheduler for outgoing traffic, it
514 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
515 later when outgoing data is available. */
516 context = (void *)client;
517 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
520 uint32 silc_client_file_send(SilcClient client,
521 SilcClientConnection conn,
522 SilcClientFileMonitor monitor,
523 void *monitor_context,
524 SilcClientEntry client_entry,
525 const char *filepath)
527 SilcClientFtpSession session;
528 SilcBuffer keyagr, ftp;
531 SILC_LOG_DEBUG(("Start"));
533 /* Check for existing session for `filepath'. */
534 silc_dlist_start(conn->ftp_sessions);
535 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
536 if (!strcmp(session->filepath, filepath) &&
537 session->client_entry == client_entry)
541 /* Add new session */
542 session = silc_calloc(1, sizeof(*session));
543 session->session_id = conn->next_session_id++;
544 session->client = client;
545 session->conn = conn;
546 session->client_entry = client_entry;
547 session->monitor = monitor;
548 session->monitor_context = monitor_context;
549 session->filepath = strdup(filepath);
550 session->server = TRUE;
551 silc_dlist_add(conn->ftp_sessions, session);
553 /* Allocate memory filesystem and put the file to it */
554 if (strrchr(filepath, '/'))
555 filename = strrchr(filepath, '/') + 1;
557 filename = (char *)filepath;
558 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
559 SILC_SFTP_FS_PERM_EXEC);
560 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
563 /* Send the key agreement inside FTP packet */
564 keyagr = silc_key_agreement_payload_encode(NULL, 0);
566 ftp = silc_buffer_alloc(1 + keyagr->len);
567 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
568 silc_buffer_format(ftp,
570 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
572 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
573 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
574 ftp->data, ftp->len, FALSE);
576 silc_buffer_free(keyagr);
577 silc_buffer_free(ftp);
579 return session->session_id;
582 bool silc_client_file_receive(SilcClient client,
583 SilcClientConnection conn,
584 SilcClientFileMonitor monitor,
585 void *monitor_context,
586 SilcClientEntry client_entry,
589 SilcClientFtpSession session;
590 SilcBuffer keyagr, ftp;
592 SILC_LOG_DEBUG(("Start"));
594 /* Get the session */
595 silc_dlist_start(conn->ftp_sessions);
596 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
597 if (session->session_id == session_id) {
602 if (session == SILC_LIST_END) {
603 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
607 /* See if we have this session running already */
608 if (session->sftp || session->listener) {
609 SILC_LOG_DEBUG(("Session already started"));
613 session->monitor = monitor;
614 session->monitor_context = monitor_context;
615 session->client_entry = client_entry;
616 session->conn = conn;
618 /* Add the listener for the key agreement */
619 session->hostname = silc_net_localhost();
620 session->listener = silc_net_create_server(0, session->hostname);
621 if (session->listener < 0) {
623 SILC_LOG_DEBUG(("Could not create listener"));
626 session->port = silc_net_get_local_port(session->listener);
627 silc_schedule_task_add(client->schedule, session->listener,
628 silc_client_ftp_process_key_agreement, session,
629 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
631 /* Send the key agreement inside FTP packet */
632 keyagr = silc_key_agreement_payload_encode(NULL, 0);
634 ftp = silc_buffer_alloc(1 + keyagr->len);
635 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
636 silc_buffer_format(ftp,
638 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
640 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
641 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
642 ftp->data, ftp->len, FALSE);
644 silc_buffer_free(keyagr);
645 silc_buffer_free(ftp);
650 bool silc_client_file_close(SilcClient client,
651 SilcClientConnection conn,
655 SILC_LOG_DEBUG(("Start"));
660 /* Callback called after remote client information has been resolved.
661 This will try to find existing session for the client entry. If found
662 then continue with the key agreement protocol. If not then it means
663 this is a file transfer request and we let the application know. */
666 silc_client_ftp_resolve_cb(SilcClient client,
667 SilcClientConnection conn,
668 SilcClientEntry *clients,
669 uint32 clients_count,
672 SilcPacketContext *packet = (SilcPacketContext *)context;
673 SilcClientFtpSession session;
674 SilcKeyAgreementPayload payload;
675 SilcClientEntry client_entry;
680 SILC_LOG_DEBUG(("Start"));
685 client_entry = clients[0];
687 silc_dlist_start(conn->ftp_sessions);
688 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
689 if (session->client_entry == client_entry)
693 /* Parse the key agreement payload */
694 payload = silc_key_agreement_payload_parse(packet->buffer);
698 hostname = silc_key_agreement_get_hostname(payload);
699 port = silc_key_agreement_get_port(payload);
701 if (session == SILC_LIST_END) {
702 /* No session found, create one and let the application know about
703 incomoing file transfer request. */
705 /* Add new session */
706 session = silc_calloc(1, sizeof(*session));
707 session->session_id = conn->next_session_id++;
708 session->client = client;
709 session->conn = conn;
710 silc_dlist_add(conn->ftp_sessions, session);
712 /* Let the application know */
713 client->ops->ftp(client, conn, client_entry,
714 session->session_id, hostname, port);
716 /* If hostname was provided we'll start the key exchange now. */
717 if (hostname && port) {
721 silc_key_agreement_payload_free(payload);
728 session->hostname = strdup(hostname);
729 session->port = port;
731 /* Session exists, continue with key agreement protocol. */
732 sock = silc_client_connect_to_client(client, conn, port, hostname,
738 silc_packet_context_free(packet);
741 /* Called when file transfer packet is received. This will parse the
742 packet and give it to the file transfer protocol. */
744 void silc_client_ftp(SilcClient client,
745 SilcSocketConnection sock,
746 SilcPacketContext *packet)
748 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
752 SILC_LOG_DEBUG(("Start"));
754 /* Parse the payload */
755 ret = silc_buffer_unformat(packet->buffer,
756 SILC_STR_UI_CHAR(&type),
761 /* We support only type number 1 (== SFTP) */
765 silc_buffer_pull(packet->buffer, 1);
767 /* If we have active FTP session then give the packet to the
768 protocol processor. */
769 if (conn->active_session) {
770 /* Give it to the SFTP */
771 if (conn->active_session->server)
772 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
775 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
778 /* We don't have active session, resolve the remote client information
779 and then try to find the correct session. */
780 SilcClientID *remote_id;
782 if (packet->src_id_type != SILC_ID_CLIENT)
785 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
790 /* Resolve the client */
791 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
792 silc_client_ftp_resolve_cb,
793 silc_packet_context_dup(packet));
794 silc_free(remote_id);