From e2890b9b6f74ba4ab2f4ac67658f5c0fea4380b3 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Mon, 22 Oct 2001 10:01:03 +0000 Subject: [PATCH] updates. --- CHANGES | 5 + apps/irssi/src/silc/core/client_ops.c | 36 +- apps/irssi/src/silc/core/client_ops.h | 6 +- apps/irssi/src/silc/core/silc-servers.c | 86 +++ apps/silcd/packet_receive.c | 34 + apps/silcd/packet_receive.h | 3 + apps/silcd/server.c | 6 +- lib/silcclient/client_ftp.c | 796 ++++++++++++++++++++++++ lib/silcclient/client_ftp.h | 23 + lib/silcutil/silcnet.c | 2 +- lib/silcutil/silcnet.h | 4 +- 11 files changed, 995 insertions(+), 6 deletions(-) create mode 100644 lib/silcclient/client_ftp.c create mode 100644 lib/silcclient/client_ftp.h diff --git a/CHANGES b/CHANGES index 77ccd3a9..3072f8c5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Mon Oct 22 12:50:08 EDT 2001 Pekka Riikonen + + * Relay the SILC_PACKET_FTP in the server. Affected files + silcd/server.c and silcd/packet_receive.c. + Sun Oct 21 20:21:02 EDT 2001 Pekka Riikonen * Renamed silc_file_read and silc_file_write to functions diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c index b832c49e..03c60cf6 100644 --- a/apps/irssi/src/silc/core/client_ops.c +++ b/apps/irssi/src/silc/core/client_ops.c @@ -84,6 +84,8 @@ void silc_channel_message(SilcClient client, SilcClientConnection conn, SILC_NICK_REC *nick; SILC_CHANNEL_REC *chanrec; + SILC_LOG_DEBUG(("Start")); + server = conn == NULL ? NULL : conn->context; chanrec = silc_channel_find_entry(server, channel); if (!chanrec) @@ -128,6 +130,8 @@ void silc_private_message(SilcClient client, SilcClientConnection conn, SILC_SERVER_REC *server; char userhost[256]; + SILC_LOG_DEBUG(("Start")); + server = conn == NULL ? NULL : conn->context; memset(userhost, 0, sizeof(userhost)); if (sender->username) @@ -177,6 +181,8 @@ void silc_notify(SilcClient client, SilcClientConnection conn, SILC_SERVER_REC *server; va_list va; + SILC_LOG_DEBUG(("Start")); + server = conn == NULL ? NULL : conn->context; va_start(va, type); @@ -226,6 +232,8 @@ void silc_disconnect(SilcClient client, SilcClientConnection conn) { SILC_SERVER_REC *server = conn->context; + SILC_LOG_DEBUG(("Start")); + if (server->conn) { nicklist_rename_unique(SERVER(server), server->conn->local_entry, server->nick, @@ -255,6 +263,8 @@ void silc_command(SilcClient client, SilcClientConnection conn, { SILC_SERVER_REC *server = conn->context; + SILC_LOG_DEBUG(("Start")); + if (!success) return; @@ -385,6 +395,8 @@ silc_command_reply(SilcClient client, SilcClientConnection conn, va_start(vp, status); + SILC_LOG_DEBUG(("Start")); + switch(command) { case SILC_COMMAND_WHOIS: { @@ -1063,6 +1075,8 @@ static void silc_get_auth_method_callback(SilcClient client, { InternalGetAuthMethod internal = (InternalGetAuthMethod)context; + SILC_LOG_DEBUG(("Start")); + switch (auth_meth) { case SILC_AUTH_NONE: /* No authentication required. */ @@ -1097,6 +1111,8 @@ void silc_get_auth_method(SilcClient client, SilcClientConnection conn, { InternalGetAuthMethod internal; + SILC_LOG_DEBUG(("Start")); + /* XXX must resolve from configuration whether this connection has any specific authentication data */ @@ -1122,6 +1138,8 @@ void silc_get_auth_method(SilcClient client, SilcClientConnection conn, void silc_failure(SilcClient client, SilcClientConnection conn, SilcProtocol protocol, void *failure) { + SILC_LOG_DEBUG(("Start")); + if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) { SilcSKEStatus status = (SilcSKEStatus)failure; @@ -1171,12 +1189,14 @@ void silc_failure(SilcClient client, SilcClientConnection conn, silc_client_perform_key_agreement). */ int silc_key_agreement(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, char *hostname, + SilcClientEntry client_entry, const char *hostname, uint16 port, SilcKeyAgreementCallback *completion, void **context) { char portstr[12]; + SILC_LOG_DEBUG(("Start")); + /* We will just display the info on the screen and return FALSE and user will have to start the key agreement with a command. */ @@ -1197,6 +1217,19 @@ int silc_key_agreement(SilcClient client, SilcClientConnection conn, return FALSE; } +void silc_ftp(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, uint32 session_id, + const char *hostname, uint16 port) +{ + + SILC_LOG_DEBUG(("Start")); + + /* XXX */ + silc_client_file_receive(client, conn, NULL, NULL, client_entry, + session_id); + +} + /* SILC client operations */ SilcClientOperations ops = { silc_say, @@ -1212,4 +1245,5 @@ SilcClientOperations ops = { silc_ask_passphrase, silc_failure, silc_key_agreement, + silc_ftp, }; diff --git a/apps/irssi/src/silc/core/client_ops.h b/apps/irssi/src/silc/core/client_ops.h index c231cddb..b7788ed7 100644 --- a/apps/irssi/src/silc/core/client_ops.h +++ b/apps/irssi/src/silc/core/client_ops.h @@ -53,7 +53,11 @@ void silc_get_auth_method(SilcClient client, SilcClientConnection conn, void silc_failure(SilcClient client, SilcClientConnection conn, SilcProtocol protocol, void *failure); int silc_key_agreement(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, char *hostname, + SilcClientEntry client_entry, const char *hostname, uint16 port, SilcKeyAgreementCallback *completion, void **context); +void silc_ftp(SilcClient client, SilcClientConnection conn, + SilcClientEntry client_entry, uint32 session_id, + const char *hostname, uint16 port); + #endif diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index 35243090..210740a1 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -399,6 +399,90 @@ static void event_text(const char *line, SILC_SERVER_REC *server, signal_stop(); } +typedef struct { + SILC_SERVER_REC *server; + char *data; + WI_ITEM_REC *item; +} *FileGetClients; + +SILC_CLIENT_CMD_FUNC(file_get_clients) +{ + FileGetClients internal = (FileGetClients)context; + signal_emit("command file", 3, internal->data, internal->server, + internal->item); + silc_free(internal->data); + silc_free(internal); +} + +static void command_file(const char *data, SILC_SERVER_REC *server, + WI_ITEM_REC *item) +{ + SilcClientConnection conn; + SilcClientEntry client_entry; + char *nickname, *tmp; + unsigned char **argv; + uint32 argc; + uint32 *argv_lens, *argv_types; + int type; + + if (!server || !IS_SILC_SERVER(server) || !server->connected) + cmd_return_error(CMDERR_NOT_CONNECTED); + + conn = server->conn; + + /* Now parse all arguments */ + tmp = g_strconcat("KEY", " ", data, NULL); + silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 4); + g_free(tmp); + + type = 0; + if (!strcasecmp(argv[1], "send")) + type = 1; + if (!strcasecmp(argv[1], "receive")) + type = 2; + + /* Parse the typed nickname. */ + if (!silc_parse_userfqdn(argv[3], &nickname, NULL)) { + printformat_module("fe-common/silc", server, NULL, + MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]); + return; + } + + /* Find client entry */ + client_entry = silc_idlist_get_client(silc_client, conn, nickname, + argv[3], TRUE); + if (!client_entry) { + FileGetClients inter = silc_calloc(1, sizeof(*inter)); + inter->server = server; + inter->data = strdup(data); + inter->item = item; + + /* Client entry not found, it was requested thus mark this to be + pending command. */ + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + NULL, silc_client_command_file_get_clients, + inter); + goto out; + } + + switch (type) { + case 1: + silc_client_file_send(silc_client, conn, NULL, NULL, client_entry, + argv[2]); + break; + + case 2: + break; + + default: + break; + } + + out: + silc_free(nickname); +} + void silc_server_init(void) { silc_servers_reconnect_init(); @@ -428,6 +512,7 @@ void silc_server_init(void) command_bind("shutdown", MODULE_NAME, (SIGNAL_FUNC) command_self); command_bind("getkey", MODULE_NAME, (SIGNAL_FUNC) command_self); command_bind("sconnect", MODULE_NAME, (SIGNAL_FUNC) command_sconnect); + command_bind("file", MODULE_NAME, (SIGNAL_FUNC) command_file); command_set_options("connect", "+silcnet"); } @@ -461,4 +546,5 @@ void silc_server_deinit(void) command_unbind("shutdown", (SIGNAL_FUNC) command_self); command_unbind("getkey", (SIGNAL_FUNC) command_self); command_unbind("sconnect", (SIGNAL_FUNC) command_sconnect); + command_unbind("file", (SIGNAL_FUNC) command_file); } diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index 5c73655e..dff4f079 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -2433,3 +2433,37 @@ void silc_server_rekey(SilcServer server, /* Run the protocol */ silc_protocol_execute(protocol, server->schedule, 0, 0); } + +/* Received file transger packet. This packet is never for us. It is to + the client in the packet's destination ID. Sending of this sort of packet + equals sending private message, ie. it is sent point to point from + one client to another. */ + +void silc_server_ftp(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcSocketConnection dst_sock; + SilcIDListData idata; + + SILC_LOG_DEBUG(("Start")); + + if (packet->src_id_type != SILC_ID_CLIENT || + packet->dst_id_type != SILC_ID_CLIENT) + return; + + if (!packet->dst_id) + return; + + /* Get the route to the client */ + dst_sock = silc_server_get_client_route(server, packet->dst_id, + packet->dst_id_len, NULL, &idata); + if (!dst_sock) + return; + + /* Relay the packet */ + silc_server_relay_packet(server, dst_sock, idata->send_key, + idata->hmac_send, idata->psn_send++, + packet, FALSE); +} + diff --git a/apps/silcd/packet_receive.h b/apps/silcd/packet_receive.h index 0f14ce04..847c131f 100644 --- a/apps/silcd/packet_receive.h +++ b/apps/silcd/packet_receive.h @@ -75,5 +75,8 @@ void silc_server_connection_auth_request(SilcServer server, void silc_server_rekey(SilcServer server, SilcSocketConnection sock, SilcPacketContext *packet); +void silc_server_ftp(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); #endif diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 869d6450..f7918f7d 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -2096,7 +2096,11 @@ void silc_server_packet_parse_type(SilcServer server, break; case SILC_PACKET_FTP: - /* Ignored */ + /* FTP packet */ + SILC_LOG_DEBUG(("FTP packet")); + if (packet->flags & SILC_PACKET_FLAG_LIST) + break; + silc_server_ftp(server, sock, packet); break; case SILC_PACKET_RESUME_ROUTER: diff --git a/lib/silcclient/client_ftp.c b/lib/silcclient/client_ftp.c new file mode 100644 index 00000000..0e83768b --- /dev/null +++ b/lib/silcclient/client_ftp.c @@ -0,0 +1,796 @@ +/* + + client_ftp.c + + Author: Pekka Riikonen + + Copyright (C) 2001 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* $Id$ */ + +#include "clientlibincludes.h" +#include "client_internal.h" + +static int +silc_client_connect_to_client(SilcClient client, + SilcClientConnection conn, int port, + char *host, void *context); +static int +silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx); +SILC_TASK_CALLBACK(silc_client_ftp_connected); +static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session, + int sock); +static void silc_client_ftp_session_free(SilcClientFtpSession session); + +/* File transmission session */ +struct SilcClientFtpSessionStruct { + uint32 session_id; + SilcClient client; + SilcClientConnection conn; + SilcClientEntry client_entry; + + char *hostname; + uint16 port; + int listener; + + SilcClientFileMonitor monitor; + void *monitor_context; + char *filepath; + + SilcSFTP sftp; + SilcSFTPFilesystem fs; + bool server; + + SilcSFTPHandle dir_handle; + SilcSFTPHandle read_handle; + uint64 filesize; + uint64 read_offset; + int fd; +}; + +/* SFTP packet send callback */ + +static void silc_client_ftp_send_packet(SilcSocketConnection sock, + SilcBuffer packet, void *context) +{ + SilcClientFtpSession session = (SilcClientFtpSession)context; + SilcClient client = session->client; + + SILC_LOG_DEBUG(("Start")); + + /* Send the packet immediately */ + silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL, + packet->data, packet->len, TRUE); +} + +/* Returns the read data */ + +static void silc_client_ftp_data(SilcSFTP sftp, + SilcSFTPStatus status, + const unsigned char *data, + uint32 data_len, + void *context) +{ + SilcClientFtpSession session = (SilcClientFtpSession)context; + + SILC_LOG_DEBUG(("Start")); + + if (status == SILC_SFTP_STATUS_EOF) { + + /* Close the handle */ + silc_sftp_close(sftp, session->read_handle, NULL, NULL); + session->read_handle = NULL; + + /* Close the read file descriptor */ + silc_file_close(session->fd); + return; + } + + if (status != SILC_SFTP_STATUS_OK) { + /* XXX errror */ + + /* Close the handle */ + silc_sftp_close(sftp, session->read_handle, NULL, NULL); + session->read_handle = NULL; + + /* Close the read file descriptor */ + silc_file_close(session->fd); + return; + } + + /* Read more, until EOF is received */ + session->read_offset += data_len; + silc_sftp_read(sftp, session->read_handle, session->read_offset, 16384, + silc_client_ftp_data, session); + + /* Call monitor callback */ + if (session->monitor) + (*session->monitor)(session->client, session->conn, + SILC_CLIENT_FILE_MONITOR_RECEIVE, + session->read_offset, session->filesize, + session->client_entry, session->session_id, + session->filepath, session->monitor_context); + + /* Write the read data */ + silc_file_write(session->fd, data, data_len); +} + +static void silc_client_ftp_open_handle(SilcSFTP sftp, + SilcSFTPStatus status, + SilcSFTPHandle handle, + void *context) +{ + SilcClientFtpSession session = (SilcClientFtpSession)context; + + SILC_LOG_DEBUG(("Start")); + + if (status != SILC_SFTP_STATUS_OK) { + /* XXX errror */ + } + + /* Open the actual local file */ + session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT); + if (session->fd < 0) { + /* XXX errror */ + } + + /* Now, start reading the file */ + silc_sftp_read(sftp, handle, session->read_offset, 16384, + silc_client_ftp_data, session); + + /* Call monitor callback */ + if (session->monitor) + (*session->monitor)(session->client, session->conn, + SILC_CLIENT_FILE_MONITOR_RECEIVE, + session->read_offset, session->filesize, + session->client_entry, session->session_id, + session->filepath, session->monitor_context); +} + +/* Returns the file name available for download. */ + +static void silc_client_ftp_readdir_name(SilcSFTP sftp, + SilcSFTPStatus status, + const SilcSFTPName name, + void *context) +{ + SilcClientFtpSession session = (SilcClientFtpSession)context; + SilcSFTPAttributesStruct attr; + + SILC_LOG_DEBUG(("Start")); + + if (status != SILC_SFTP_STATUS_OK) { + /* XXX errror */ + } + + /* Now open the file */ + memset(&attr, 0, sizeof(attr)); + silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr, + silc_client_ftp_open_handle, session); + + /* Save the important attributes */ + session->filepath = strdup(name->filename[0]); + session->filesize = name->attrs[0]->size; + + /* Close the directory handle */ + silc_sftp_close(sftp, session->dir_handle, NULL, NULL); + session->dir_handle = NULL; +} + +/* Returns the file handle after giving opendir command. */ + +static void silc_client_ftp_opendir_handle(SilcSFTP sftp, + SilcSFTPStatus status, + SilcSFTPHandle handle, + void *context) +{ + SilcClientFtpSession session = (SilcClientFtpSession)context; + + SILC_LOG_DEBUG(("Start")); + + if (status != SILC_SFTP_STATUS_OK) { + /* XXX errror */ + } + + /* Now, read the directory */ + silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session); + session->dir_handle = handle; +} + +/* SFTP version callback for SFTP client */ + +static void silc_client_ftp_version(SilcSFTP sftp, + SilcSFTPStatus status, + SilcSFTPVersion version, + void *context) +{ + SilcClientFtpSession session = (SilcClientFtpSession)context; + + SILC_LOG_DEBUG(("Start")); + + if (status != SILC_SFTP_STATUS_OK) { + /* XXX errror */ + } + + /* The SFTP session is open, now retrieve the info about available file. */ + silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session); +} + +/* This callback is called after the key agreement protocol has been + performed. This calls the final completion callback for the application. */ + +SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientKEInternalContext *ctx = + (SilcClientKEInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + SilcClientFtpSession session = (SilcClientFtpSession)ctx->context; + SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_ERROR || + protocol->state == SILC_PROTOCOL_STATE_FAILURE) { + /* Error occured during protocol */ + silc_ske_free_key_material(ctx->keymat); + goto out; + } + + /* Set keys into use */ + silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat, + ctx->ske->prop->cipher, + ctx->ske->prop->pkcs, + ctx->ske->prop->hash, + ctx->ske->prop->hmac, + ctx->ske->prop->group); + + /* If we are the SFTP client then start the SFTP session and retrieve + the info about the file available for download. */ + if (!session->server) { + session->sftp = silc_sftp_client_start(conn->sock, + silc_client_ftp_send_packet, + session, + silc_client_ftp_version, session); + } + + out: + silc_ske_free_key_material(ctx->keymat); + if (ctx->ske) + silc_ske_free(ctx->ske); + silc_free(ctx->dest_id); + silc_socket_free(ctx->sock); + silc_free(ctx); + ctx->sock->protocol = NULL; + silc_protocol_free(protocol); +} + +static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session, + int sock) +{ + SilcClient client = session->client; + SilcClientKEInternalContext *proto_ctx; + SilcProtocol protocol; + SilcClientConnection conn; + void *context; + + SILC_LOG_DEBUG(("Start")); + + /* Add new connection for this session */ + conn = silc_client_add_connection(client, session->hostname, + session->port, session); + + /* Allocate new socket connection object */ + silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &conn->sock); + conn->sock->hostname = strdup(session->hostname); + conn->sock->port = silc_net_get_remote_port(sock); + + /* Allocate the SFTP */ + if (session->server) + session->sftp = silc_sftp_server_start(conn->sock, + silc_client_ftp_send_packet, + session, session->fs); + + /* Allocate internal context for key exchange protocol. This is + sent as context for the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->client = client; + proto_ctx->sock = silc_socket_dup(conn->sock); + proto_ctx->rng = client->rng; + proto_ctx->responder = FALSE; + proto_ctx->context = session; + proto_ctx->send_packet = silc_client_protocol_ke_send_packet; + proto_ctx->verify = silc_client_protocol_ke_verify_key; + + /* Perform key exchange protocol. */ + silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, + &protocol, (void *)proto_ctx, + silc_client_ftp_key_agreement_final); + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as well. + However, this doesn't set the scheduler for outgoing traffic, it will + be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + context = (void *)client; + SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock); + + /* Execute the protocol */ + silc_protocol_execute(protocol, client->schedule, 0, 0); +} + +SILC_TASK_CALLBACK(silc_client_ftp_connected) +{ + SilcClientInternalConnectContext *ctx = + (SilcClientInternalConnectContext *)context; + SilcClient client = ctx->client; + SilcClientConnection conn = ctx->conn; + SilcClientFtpSession session = (SilcClientFtpSession)ctx->context; + int opt, opt_len = sizeof(opt); + + SILC_LOG_DEBUG(("Start")); + + /* Check the socket status as it might be in error */ + silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len); + if (opt != 0) { + if (ctx->tries < 2) { + /* Connection failed but lets try again */ + client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Could not connect to client %s: %s", + ctx->host, strerror(opt)); + client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, + "Connecting to port %d of client %s resumed", + ctx->port, ctx->host); + + /* Unregister old connection try */ + silc_schedule_unset_listen_fd(client->schedule, fd); + silc_net_close_connection(fd); + silc_schedule_task_del(client->schedule, ctx->task); + + /* Try again */ + silc_client_connect_to_client_internal(ctx); + ctx->tries++; + } else { + /* Connection failed and we won't try anymore */ + client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Could not connect to client %s: %s", + ctx->host, strerror(opt)); + silc_schedule_unset_listen_fd(client->schedule, fd); + silc_net_close_connection(fd); + silc_schedule_task_del(client->schedule, ctx->task); + silc_free(ctx); + silc_client_ftp_session_free(session); + } + return; + } + + silc_schedule_unset_listen_fd(client->schedule, fd); + silc_schedule_task_del(client->schedule, ctx->task); + + /* Start the key agreement */ + silc_client_ftp_start_key_agreement(session, fd); +} + +static int +silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx) +{ + int sock; + + /* Create connection to server asynchronously */ + sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host); + if (sock < 0) + return -1; + + /* Register task that will receive the async connect and will + read the result. */ + ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, + silc_client_ftp_connected, + (void *)ctx, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL); + silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE); + + ctx->sock = sock; + + return sock; +} + +static int +silc_client_connect_to_client(SilcClient client, + SilcClientConnection conn, int port, + char *host, void *context) +{ + SilcClientInternalConnectContext *ctx; + + /* Allocate internal context for connection process. This is + needed as we are doing async connecting. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->conn = conn; + ctx->host = strdup(host); + ctx->port = port; + ctx->tries = 0; + ctx->context = context; + + /* Do the actual connecting process */ + return silc_client_connect_to_client_internal(ctx); +} + +/* Free session */ + +static void silc_client_ftp_session_free(SilcClientFtpSession session) +{ + silc_dlist_del(session->conn->ftp_sessions, session); + + if (session->sftp) { + if (session->server) + silc_sftp_server_shutdown(session->sftp); + else + silc_sftp_client_shutdown(session->sftp); + } + + if (session->fs) + silc_sftp_fs_memory_free(session->fs); + + silc_free(session->hostname); + silc_free(session->filepath); + silc_free(session); +} + +SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement) +{ + SilcClientFtpSession session = (SilcClientFtpSession)context; + SilcClient client = session->client; + SilcClientConnection conn; + SilcSocketConnection newsocket; + SilcClientKEInternalContext *proto_ctx; + int sock; + + SILC_LOG_DEBUG(("Start")); + + sock = silc_net_accept_connection(session->listener); + if (sock < 0) { + /* XXX error */ + return; + } + + /* Set socket options */ + silc_net_set_socket_nonblock(sock); + silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + + /* Allocate new socket connection object */ + silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); + + /* Perform name and address lookups for the remote host. */ + silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip); + if (!newsocket->hostname && !newsocket->ip) { + /* XXX error */ + return; + } + if (!newsocket->hostname) + newsocket->hostname = strdup(newsocket->ip); + newsocket->port = silc_net_get_remote_port(sock); + + /* Add new connection for this session */ + conn = silc_client_add_connection(client, newsocket->hostname, + newsocket->port, session); + conn->sock = newsocket; + conn->sock->user_data = conn; + + /* Allocate internal context for key exchange protocol. This is + sent as context for the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->client = client; + proto_ctx->sock = silc_socket_dup(conn->sock); + proto_ctx->rng = client->rng; + proto_ctx->responder = TRUE; + proto_ctx->context = session; + proto_ctx->send_packet = silc_client_protocol_ke_send_packet; + proto_ctx->verify = silc_client_protocol_ke_verify_key; + + /* Prepare the connection for key exchange protocol. We allocate the + protocol but will not start it yet. The connector will be the + initiator of the protocol thus we will wait for initiation from + there before we start the protocol. */ + silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, + &newsocket->protocol, proto_ctx, + silc_client_ftp_key_agreement_final); + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as well. + However, this doesn't set the scheduler for outgoing traffic, it + will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + context = (void *)client; + SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock); +} + +uint32 silc_client_file_send(SilcClient client, + SilcClientConnection conn, + SilcClientFileMonitor monitor, + void *monitor_context, + SilcClientEntry client_entry, + const char *filepath) +{ + SilcClientFtpSession session; + SilcBuffer keyagr, ftp; + char *filename; + + SILC_LOG_DEBUG(("Start")); + + /* Check for existing session for `filepath'. */ + silc_dlist_start(conn->ftp_sessions); + while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) { + if (!strcmp(session->filepath, filepath) && + session->client_entry == client_entry) + return 0; + } + + /* Add new session */ + session = silc_calloc(1, sizeof(*session)); + session->session_id = conn->next_session_id++; + session->client = client; + session->conn = conn; + session->client_entry = client_entry; + session->monitor = monitor; + session->monitor_context = monitor_context; + session->filepath = strdup(filepath); + session->server = TRUE; + silc_dlist_add(conn->ftp_sessions, session); + + /* Allocate memory filesystem and put the file to it */ + if (strrchr(filepath, '/')) + filename = strrchr(filepath, '/') + 1; + else + filename = (char *)filepath; + session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ | + SILC_SFTP_FS_PERM_EXEC); + silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ, + filename, filepath); + + /* Send the key agreement inside FTP packet */ + keyagr = silc_key_agreement_payload_encode(NULL, 0); + + ftp = silc_buffer_alloc(1 + keyagr->len); + silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp)); + silc_buffer_format(ftp, + SILC_STR_UI_CHAR(1), + SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len), + SILC_STR_END); + silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP, + client_entry->id, SILC_ID_CLIENT, NULL, NULL, + ftp->data, ftp->len, FALSE); + + silc_buffer_free(keyagr); + silc_buffer_free(ftp); + + return session->session_id; +} + +bool silc_client_file_receive(SilcClient client, + SilcClientConnection conn, + SilcClientFileMonitor monitor, + void *monitor_context, + SilcClientEntry client_entry, + uint32 session_id) +{ + SilcClientFtpSession session; + SilcBuffer keyagr, ftp; + + SILC_LOG_DEBUG(("Start")); + + /* Get the session */ + silc_dlist_start(conn->ftp_sessions); + while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) { + if (session->session_id == session_id) { + break; + } + } + + if (session == SILC_LIST_END) { + SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id)); + return FALSE; + } + + /* See if we have this session running already */ + if (session->sftp || session->listener) { + SILC_LOG_DEBUG(("Session already started")); + return FALSE; + } + + session->monitor = monitor; + session->monitor_context = monitor_context; + session->client_entry = client_entry; + session->conn = conn; + + /* Add the listener for the key agreement */ + session->hostname = silc_net_localhost(); + session->listener = silc_net_create_server(0, session->hostname); + if (session->listener < 0) { + /* XXX Error */ + SILC_LOG_DEBUG(("Could not create listener")); + return FALSE; + } + session->port = silc_net_get_local_port(session->listener); + silc_schedule_task_add(client->schedule, session->listener, + silc_client_ftp_process_key_agreement, session, + 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL); + + /* Send the key agreement inside FTP packet */ + keyagr = silc_key_agreement_payload_encode(NULL, 0); + + ftp = silc_buffer_alloc(1 + keyagr->len); + silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp)); + silc_buffer_format(ftp, + SILC_STR_UI_CHAR(1), + SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len), + SILC_STR_END); + silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP, + client_entry->id, SILC_ID_CLIENT, NULL, NULL, + ftp->data, ftp->len, FALSE); + + silc_buffer_free(keyagr); + silc_buffer_free(ftp); + + return TRUE; +} + +bool silc_client_file_close(SilcClient client, + SilcClientConnection conn, + uint32 session_id) +{ + + SILC_LOG_DEBUG(("Start")); + + return TRUE; +} + +/* Callback called after remote client information has been resolved. + This will try to find existing session for the client entry. If found + then continue with the key agreement protocol. If not then it means + this is a file transfer request and we let the application know. */ + +static void +silc_client_ftp_resolve_cb(SilcClient client, + SilcClientConnection conn, + SilcClientEntry *clients, + uint32 clients_count, + void *context) +{ + SilcPacketContext *packet = (SilcPacketContext *)context; + SilcClientFtpSession session; + SilcKeyAgreementPayload payload; + SilcClientEntry client_entry; + char *hostname; + uint16 port; + int sock; + + SILC_LOG_DEBUG(("Start")); + + if (!clients) + goto out; + + client_entry = clients[0]; + + silc_dlist_start(conn->ftp_sessions); + while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) { + if (session->client_entry == client_entry) + break; + } + + /* Parse the key agreement payload */ + payload = silc_key_agreement_payload_parse(packet->buffer); + if (!payload) + goto out; + + hostname = silc_key_agreement_get_hostname(payload); + port = silc_key_agreement_get_port(payload); + + if (session == SILC_LIST_END) { + /* No session found, create one and let the application know about + incomoing file transfer request. */ + + /* Add new session */ + session = silc_calloc(1, sizeof(*session)); + session->session_id = conn->next_session_id++; + session->client = client; + session->conn = conn; + silc_dlist_add(conn->ftp_sessions, session); + + /* Let the application know */ + client->ops->ftp(client, conn, client_entry, + session->session_id, hostname, port); + + /* If hostname was provided we'll start the key exchange now. */ + if (hostname && port) { + /* XXX */ + } + + silc_key_agreement_payload_free(payload); + goto out; + } + + if (!hostname) + goto out; + + session->hostname = strdup(hostname); + session->port = port; + + /* Session exists, continue with key agreement protocol. */ + sock = silc_client_connect_to_client(client, conn, port, hostname, + session); + if (sock < 0) + goto out; + + out: + silc_packet_context_free(packet); +} + +/* Called when file transfer packet is received. This will parse the + packet and give it to the file transfer protocol. */ + +void silc_client_ftp(SilcClient client, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcClientConnection conn = (SilcClientConnection)sock->user_data; + uint8 type; + int ret; + + SILC_LOG_DEBUG(("Start")); + + /* Parse the payload */ + ret = silc_buffer_unformat(packet->buffer, + SILC_STR_UI_CHAR(&type), + SILC_STR_END); + if (ret == -1) + return; + + /* We support only type number 1 (== SFTP) */ + if (type != 1) + return; + + silc_buffer_pull(packet->buffer, 1); + + /* If we have active FTP session then give the packet to the + protocol processor. */ + if (conn->active_session) { + /* Give it to the SFTP */ + if (conn->active_session->server) + silc_sftp_server_receive_process(conn->active_session->sftp, sock, + packet); + else + silc_sftp_client_receive_process(conn->active_session->sftp, sock, + packet); + } else { + /* We don't have active session, resolve the remote client information + and then try to find the correct session. */ + SilcClientID *remote_id; + + if (packet->src_id_type != SILC_ID_CLIENT) + return; + + remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, + SILC_ID_CLIENT); + if (!remote_id) + return; + + /* Resolve the client */ + silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id, + silc_client_ftp_resolve_cb, + silc_packet_context_dup(packet)); + silc_free(remote_id); + } +} diff --git a/lib/silcclient/client_ftp.h b/lib/silcclient/client_ftp.h new file mode 100644 index 00000000..385a48f4 --- /dev/null +++ b/lib/silcclient/client_ftp.h @@ -0,0 +1,23 @@ +/* + + client_ftp.h + + Author: Pekka Riikonen + + Copyright (C) 2001 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef CLIENT_FTP_H +#define CLIENT_FTP_H + +#endif /* CLIENT_FTP_H */ diff --git a/lib/silcutil/silcnet.c b/lib/silcutil/silcnet.c index 35d64270..1120529e 100644 --- a/lib/silcutil/silcnet.c +++ b/lib/silcutil/silcnet.c @@ -215,7 +215,7 @@ uint16 silc_net_get_local_port(int sock) /* Return name of localhost. */ -char *silc_net_localhost() +char *silc_net_localhost(void) { char hostname[256]; struct hostent *dest; diff --git a/lib/silcutil/silcnet.h b/lib/silcutil/silcnet.h index 2cfa2430..40e138f8 100644 --- a/lib/silcutil/silcnet.h +++ b/lib/silcutil/silcnet.h @@ -280,7 +280,7 @@ uint16 silc_net_get_local_port(int sock); * * SYNOPSIS * - * char *silc_net_localhost(); + * char *silc_net_localhost(void); * * DESCRIPTION * @@ -289,7 +289,7 @@ uint16 silc_net_get_local_port(int sock); * the first found hostname is returned. * ***/ -char *silc_net_localhost(); +char *silc_net_localhost(void); #ifdef WIN32 -- 2.24.0