Files
m241Neelima/components/ip_net2-6.9/ipssh/src/ipssh_sftp_clt.c
T
2026-07-30 11:19:44 +05:30

2414 lines
74 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_sftp_clt.c,v $ $Revision: 1.36.10.3 $
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_sftp_clt.c,v $
* $Author: jren $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Roger Boden <roger@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipssh_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#ifdef IPSSH_USE_SFTP_CLT
#include "ipssh_h.h"
#include "ipssh.h"
#include "ipssh_transport.h"
#include "ipssh_buffer.h"
#include "ipssh_channel.h"
#include "ipssh_keyex_dh.h"
#include "ipssh_sftp.h"
#include "ipssh_sftp_clt.h"
#include <ipcom_clib.h>
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_err.h>
#include <ipcom_sock.h>
#include <ipcom_sock2.h>
#include <ipcom_getopt.h>
#include "openssl/pem.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/* Version 3 is the most used version on the Internet. Most notably, openssh
* speaks sftp version 3. */
#define IPSSH_SFTP_VERSION 3
#define IPSSH_CLT2SRV 1
#define IPSSH_SRV2CLT 2
#define IPSSH_SFTP_CLT_HANDLE_MAGIC 0xDEAFBEEF
#ifndef IPSSH_SFTP_CLT_MAX_REQS
#define IPSSH_SFTP_CLT_MAX_REQS 10
#endif
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
struct Ipssh_sftp_clt_dir_handle_st
{
Ip_u32 magic;
Ip_u8* ptr;
Ip_u32 len;
Ip_u32 count;
Ipcom_buffer* pkt;
Ipssh_dirent ret;
};
typedef struct Ipssh_sftp_clt_dir_handle_st Ipssh_sftp_clt_dir_handle;
struct Ipssh_sftp_clt_read_req_st
{
Ipcom_list list;
Ip_u32 req_id;
Ip_u32 len;
Ip_u64 offset;
};
typedef struct Ipssh_sftp_clt_read_req_st Ipssh_sftp_clt_read_req;
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_GLOBAL Ip_err ipssh_send_fxp_init(Ipssh_channel* channel);
IP_GLOBAL Ip_err ipssh_send_fxp_realpath(Ipssh_channel* channel, const char* path);
IP_GLOBAL Ip_err ipssh_send_fxp_opendir(Ipssh_channel* channel, const char* path);
IP_GLOBAL Ip_err ipssh_send_fxp_readdir(Ipssh_channel* channel, Ip_u8* handle, Ip_u32 handle_len);
IP_GLOBAL Ip_err ipssh_send_fxp_close(Ipssh_channel* channel, Ip_u8* handle, Ip_u32 handle_len);
IP_GLOBAL Ip_err ipssh_send_fxp_open(Ipssh_channel* channel,
const char* path,
Ip_u32 flags,
Ip_u32 attr_flags,
struct Ip_stat* file_attr);
IP_GLOBAL Ip_err ipssh_send_fxp_read(Ipssh_channel* channel, const Ip_u8* handle,
Ip_u32 handle_len, Ipssh_sftp_clt_read_req* req);
IP_GLOBAL Ip_err ipssh_read_sftp_msg(Ipssh_channel* channel);
IP_GLOBAL Ip_err ipssh_msg_fxp_version(Ipssh_channel* channel, Ipcom_buffer* pkt);
IP_GLOBAL Ip_err ipssh_read_channel_data_msg(Ipssh_channel* channel);
IP_STATIC Ip_u32 ipssh_get_sftp_req_len(Ipssh_channel* channel, int direction, Ip_u32 old_req_len);
IP_STATIC Ip_err ipssh_make_abs_path(const char *cwd, const char *path, char *abs_path, Ip_size_t abs_path_len);
IP_STATIC Ipssh_sftp_clt_read_req* ipssh_sftp_clt_id2read_req(Ipcom_list* pending_reqs, Ip_u32 req_id);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
IP_STATIC Ip_err
ipssh_sftp_clt_dispatch_read_req(Ipssh_channel* channel,
Ipcom_list* pending_reqs,
Ipssh_sftp_clt_read_req* read_req,
const Ip_u8* handle,
Ip_u32 handle_len,
Ip_u32 req_id,
Ip_u64 offset,
Ip_u32 len)
{
ip_assert(!ipssh_sftp_clt_id2read_req(pending_reqs, req_id));
if (!read_req)
{
read_req = ipcom_calloc(1, sizeof(Ipssh_sftp_clt_read_req));
if (!read_req)
return IPCOM_ERR_NO_MEMORY;
ipcom_list_insert_last(pending_reqs, &read_req->list);
}
read_req->req_id = req_id;
IP_U64_COPY(read_req->offset, offset);
read_req->len = len;
return ipssh_send_fxp_read(channel, handle, handle_len, read_req);
}
/*
*===========================================================================
* Ipssh_sftp_clt_id2read_req
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ipssh_sftp_clt_read_req*
ipssh_sftp_clt_id2read_req(Ipcom_list* pending_reqs, Ip_u32 req_id)
{
Ipssh_sftp_clt_read_req* req;
for(req = (Ipssh_sftp_clt_read_req*)ipcom_list_first(pending_reqs);
req;
req = (Ipssh_sftp_clt_read_req*)ipcom_list_next(&(req->list)))
{
if (req->req_id == req_id)
return req;
}
return IP_NULL;
}
/*
*===========================================================================
* ipssh_make_abs_path
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_make_abs_path(const char *cwd, const char *path, char *abs_path, Ip_size_t abs_path_len)
{
ip_assert(path);
ip_assert(cwd);
ip_assert(abs_path);
if(path[0] != '/')
{
if(ipcom_strlen(cwd) + ipcom_strlen(path) >= abs_path_len)
return IPCOM_ERR_INVALID_ARG;
ipcom_strncpy(abs_path, cwd, IPSSH_SFTP_MAX_PATH);
if(cwd[ipcom_strlen(cwd)-1] != '/')
ipcom_strcat(abs_path, "/");
ipcom_strcat(abs_path, path);
}
else
{
if(ipcom_strlen(path) >= abs_path_len)
return IPCOM_ERR_INVALID_ARG;
ipcom_strcpy(abs_path, path);
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_get_sftp_req_len
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_u32
ipssh_get_sftp_req_len(Ipssh_channel* channel, int direction, Ip_u32 old_req_len)
{
Ip_u32 req_len = 16*1024-13; /* openssh uses a ch msg max size of 16k for sftp
* connections although a larger value typically
* is published in the
* SSH_MSG_CHANNEL_OPEN_CONFIRMATION.
* -13: We need to reserve space for sftp msg len
* and msg type, channel id and channel msg length */
switch(direction)
{
case IPSSH_CLT2SRV:
req_len = (Ip_u32)IP_MIN((Ip_s32)req_len, channel->peer_win);
req_len = IP_MIN(req_len, channel->peer_max_pkt_size);
break;
case IPSSH_SRV2CLT:
req_len = (Ip_u32)IP_MIN((Ip_s32)req_len, channel->my_win);
req_len = IP_MIN(req_len, channel->my_max_pkt_size);
break;
default:
IP_PANIC();
}
if (old_req_len == 0)
return req_len;
return IP_MIN(old_req_len, req_len);
}
/*
*===========================================================================
* ipssh_sftp_clt_wait_for_channel_win_adjust_msg
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_sftp_clt_wait_for_channel_win_adjust_msg(Ipssh_channel* channel)
{
Ipssh_conn* ssh_conn;
ssh_conn = channel->ssh_conn;
if(ipssh_read_v2_pkt(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_wait_for_channel_win_adjust_msg :: ipssh_read_v2_pkt() failed");
return IPCOM_ERR_FAILED;
}
if(IPSSH_GET_PKT_TYPE(ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf)) == IPSSH_MSG_CHANNEL_WINDOW_ADJUST)
{
if(ipssh_pkt_dispatch(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_wait_for_channel_win_adjust_msg :: ipssh_pkt_dispatch() failed");
return IPCOM_ERR_FAILED;
}
}
else
{
IPCOM_LOG1(ERR, "ipssh_sftp_clt_wait_for_channel_win_adjust_msg :: unexpected msg received: %d",
IPSSH_GET_PKT_TYPE(ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf)));
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_check_ch_win
*===========================================================================
* Description: Checks that we have a big enough channel window to send
* an sftp message. If the window size is too small, we wait for
* an IPSSH_MSG_CHANNEL_WINDOW_ADJUST msg.
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipssh_sftp_clt_check_ch_win(Ipssh_channel* channel, int min_win_size)
{
while (1)
{
if (channel->peer_win < min_win_size)
{
if (ipssh_sftp_clt_wait_for_channel_win_adjust_msg(channel) != IPCOM_SUCCESS)
return;
}
else
return;
}
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
IP_GLOBAL Ip_err
ipssh_read_channel_data_msg(Ipssh_channel* channel)
{
Ipssh_conn* ssh_conn;
Ip_u8 msg_type;
Ip_err rc;
ssh_conn = channel->ssh_conn;
while (1)
{
if(ipssh_read_v2_pkt(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_read_channel_data_msg :: ipssh_read_v2_pkt() failed");
return IPCOM_ERR_FAILED;
}
msg_type = IPSSH_GET_PKT_TYPE(ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf));
if(msg_type == IPSSH_MSG_CHANNEL_DATA)
return IPCOM_SUCCESS;
if(ipssh_pkt_dispatch(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_read_channel_data_msg :: ipssh_pkt_dispatch() failed");
return IPCOM_ERR_FAILED;
}
if (msg_type == IPSSH_MSG_KEXINIT)
{
IPCOM_LOG0(INFO, "ipssh_read_channel_data_msg :: rekeying requested by peer");
switch(ssh_conn->conn_prop_pending.kex_alg[0])
{
case IPSSH_KEYEX_DH_GR1_SHA1_ID:
rc = ipssh_do_keyex_dh_clt(ssh_conn);
if (rc != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_read_channel_data_msg :: ipssh_do_keyex_dh_clt() failed");
return rc;
}
break;
default:
IP_PANIC();
return IPCOM_ERR_FAILED;
}
}
}
}
/*
*===========================================================================
* ipssh_read_sftp_msg
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_read_sftp_msg(Ipssh_channel* channel)
{
Ipssh_conn* ssh_conn;
Ip_u32 ch_id = 0;
Ip_u32 ch_data_len = 0;
Ip_u32 sftp_msg_len;
ssh_conn = channel->ssh_conn;
while(1)
{
if (!channel->data.sftp.msg) /* No sftp msg data read */
{
if(ipssh_read_channel_data_msg(channel) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: ipssh_read_channel_data_msg() failed");
return IPCOM_ERR_FAILED;
}
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_data_len))
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: failed to unpack channel data msg");
return IPCOM_ERR_FAILED;
}
if(ipssh_send_channel_win_adjust_msg(channel, ch_data_len))
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: ipssh_send_channel_win_adjust failed");
return IPCOM_ERR_FAILED;
}
sftp_msg_len = IP_GET_NTOHL(ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf))+4;
/* Exactly one complete sftp msg received */
if ((sftp_msg_len) == ipcom_buffer_len(ssh_conn->recv_plain_buf))
{
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 4))
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: failed to unpack sftp msg len");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/* More than one complete sftp msg received */
if (ipcom_buffer_len(ssh_conn->recv_plain_buf) > sftp_msg_len)
{
Ip_u32 next_sftp_frag_len;
next_sftp_frag_len = ipcom_buffer_len(ssh_conn->recv_plain_buf) - (sftp_msg_len);
channel->data.sftp.msg = ipcom_buffer_new(next_sftp_frag_len);
if (!channel->data.sftp.msg)
return IPCOM_ERR_NO_MEMORY;
(void)ipcom_buffer_put(channel->data.sftp.msg,
(Ip_u8*)ipcom_buffer_ptr2pos(ssh_conn->recv_plain_buf, sftp_msg_len),
next_sftp_frag_len);
(void)ipcom_buffer_consume_end(ssh_conn->recv_plain_buf, next_sftp_frag_len);
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 4); /* Skip sftp msg len */
return IPCOM_SUCCESS;
}
/* The sftp message is fragmented into several ch data msgs */
if(sftp_msg_len > ipcom_buffer_len(ssh_conn->recv_plain_buf))
{
channel->data.sftp.msg = ipcom_buffer_new(sftp_msg_len);
if (!channel->data.sftp.msg)
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: out of memory");
return IPCOM_ERR_NO_MEMORY;
}
(void)ipcom_buffer_put(channel->data.sftp.msg,
ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf),
ipcom_buffer_len(ssh_conn->recv_plain_buf));
}
}
if(channel->data.sftp.msg) /* sftp msg data already read */
{
Ipcom_buffer* tmp_buf = IP_NULL;
sftp_msg_len = IP_GET_NTOHL(ipcom_buffer_start_ptr(channel->data.sftp.msg))+4;
/* We now have a complete sftp message in channel->data.sftp.msg */
if (ipcom_buffer_len(channel->data.sftp.msg) == sftp_msg_len)
{
ipcom_buffer_free(ssh_conn->recv_plain_buf);
ssh_conn->recv_plain_buf = channel->data.sftp.msg;
channel->data.sftp.msg = IP_NULL;
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &sftp_msg_len))
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: failed to unpack sftp msg len");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/* More than one sftp msg in channel->data.sftp.msg */
if (ipcom_buffer_len(channel->data.sftp.msg) > sftp_msg_len)
{
Ip_u32 next_sftp_msg_frag;
next_sftp_msg_frag = ipcom_buffer_len(channel->data.sftp.msg) - (sftp_msg_len);
tmp_buf = ipcom_buffer_new(next_sftp_msg_frag);
if (!tmp_buf)
{
IPCOM_LOG1(ERR, "ipssh_read_sftp_msg :: failed to allocate %d bytes", next_sftp_msg_frag);
return IPCOM_ERR_NO_MEMORY;
}
(void)ipcom_buffer_put(tmp_buf,
(Ip_u8*)ipcom_buffer_ptr2pos(channel->data.sftp.msg, sftp_msg_len),
next_sftp_msg_frag);
ssh_conn->recv_plain_buf = channel->data.sftp.msg;
channel->data.sftp.msg = tmp_buf;
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, 4);
(void)ipcom_buffer_consume_end(ssh_conn->recv_plain_buf, next_sftp_msg_frag);
return IPCOM_SUCCESS;
}
/* If we get here we only have a fragment of the sftp msg. Read a channel data msg */
if(ipssh_read_channel_data_msg(channel) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: ipssh_read_channel_data_msg() failed");
return IPCOM_ERR_FAILED;
}
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_data_len))
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: failed to unpack channel data msg");
return IPCOM_ERR_FAILED;
}
if(ipssh_send_channel_win_adjust_msg(channel, ch_data_len))
{
IPCOM_LOG0(ERR, "ipssh_read_sftp_msg :: ipssh_send_channel_win_adjust failed");
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf), ch_data_len);
}
}
}
/*
*===========================================================================
* ipssh_send_fxp_readdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_readdir(Ipssh_channel* channel, Ip_u8* handle, Ip_u32 handle_len)
{
Ip_err rc = IPCOM_ERR_FAILED;
Ipcom_buffer* pkt;
pkt = ipssh_buffer_new_sftp_pkt(32+handle_len, IPSSH_FXP_READDIR);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_readdir :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshbuf(pkt, handle, handle_len);
rc = ipssh_send_fxp_msg(channel, pkt);
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_close
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_close(Ipssh_channel* channel, Ip_u8* handle, Ip_u32 handle_len)
{
Ip_err rc = IPCOM_ERR_FAILED;
Ipcom_buffer* pkt;
Ip_u8 pkt_type;
Ip_u32 req_id;
Ip_u32 status;
pkt = ipssh_buffer_new_sftp_pkt(32+handle_len, IPSSH_FXP_CLOSE);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_close :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshbuf(pkt, handle, handle_len);
if(ipssh_send_fxp_msg(channel, pkt))
goto cleanup;
/* Read reply */
if(ipssh_read_sftp_msg(channel))
goto cleanup;
if(ipcom_buffer_get_char(channel->ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
if(pkt_type == IPSSH_FXP_STATUS)
{
if(ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_close :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
if(status == IPSSH_FX_OK)
rc = IPCOM_SUCCESS;
}
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_opendir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_opendir(Ipssh_channel* channel, const char* path)
{
Ip_err rc = IPCOM_ERR_FAILED;
Ipcom_buffer* pkt;
pkt = ipssh_buffer_new_sftp_pkt(32+ipcom_strlen(path), IPSSH_FXP_OPENDIR);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_opendir :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshstr(pkt, path);
rc = ipssh_send_fxp_msg(channel, pkt);
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_realpath
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_realpath(Ipssh_channel* channel, const char* path)
{
Ip_err rc;
Ipcom_buffer* pkt;
IPCOM_LOG0(DEBUG, "ipssh_send_fxp_realpath ::");
pkt = ipssh_buffer_new_sftp_pkt(32+ipcom_strlen(path), IPSSH_FXP_REALPATH);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_realpath :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshstr(pkt, path);
rc = ipssh_send_fxp_msg(channel, pkt);
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_init
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_init(Ipssh_channel* channel)
{
Ip_err rc = IPCOM_ERR_FAILED;
Ipcom_buffer* pkt;
IPCOM_LOG0(DEBUG, "ipssh_send_fxp_init ::");
pkt = ipssh_buffer_new_sftp_pkt(32, IPSSH_FXP_INIT);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_init :: out of memory");
return IPCOM_ERR_FAILED;
}
(void)ipssh_buffer_put_int(pkt, IPSSH_SFTP_VERSION);
if(ipssh_send_fxp_msg(channel, pkt))
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_init :: ipssh_send_pkt failed");
goto cleanup;
}
channel->state = IPSSH_CH_STATE_CLT_FXP_INIT_SENT;
rc = IPCOM_SUCCESS;
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_open
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_open(Ipssh_channel* channel, const char* path, Ip_u32 flags, Ip_u32 attr_flags, struct Ip_stat* file_attr)
{
Ipcom_buffer* pkt;
Ip_err rc = IPCOM_ERR_FAILED;
pkt = ipssh_buffer_new_sftp_pkt(32 + ipcom_strlen(path), IPSSH_FXP_OPEN);
if(!pkt)
return IPCOM_ERR_NO_MEMORY;
channel->data.sftp.req_id++;
if(ipssh_buffer_put_uint(pkt, channel->data.sftp.req_id)
|| ipssh_buffer_put_sshstr(pkt, path)
|| ipssh_buffer_put_uint(pkt, flags))
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_open :: failed to pack msg");
goto cleanup;
}
if(ipssh_pack_attr(pkt, file_attr, attr_flags) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_open :: ipssh_pack_attr failed");
goto cleanup;
}
rc = ipssh_send_fxp_msg(channel, pkt);
cleanup:
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_read
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_read(Ipssh_channel* channel, const Ip_u8* handle, Ip_u32 handle_len, Ipssh_sftp_clt_read_req* req)
{
Ipcom_buffer* pkt;
Ip_err rc = IPCOM_ERR_FAILED;
pkt = ipssh_buffer_new_sftp_pkt(32 + handle_len, IPSSH_FXP_READ);
if(!pkt)
return IPCOM_ERR_NO_MEMORY;
if(ipssh_buffer_put_uint(pkt, req->req_id)
|| ipssh_buffer_put_sshbuf(pkt, handle, handle_len)
|| ipssh_buffer_put_u64(pkt, req->offset)
|| ipssh_buffer_put_uint(pkt, req->len))
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_read :: failed to pack msg");
ipcom_buffer_free(pkt);
return IPCOM_ERR_FAILED;
}
rc = ipssh_send_fxp_msg(channel, pkt);
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_version
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_version(Ipssh_channel* channel, Ipcom_buffer* pkt)
{
Ip_u32 version;
Ip_u8* len_ptr;
int ext_len;
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_version ::");
/* Get length field so we can figure out if the packet contains any
* extension data */
len_ptr = ipcom_buffer_start_ptr(pkt);
len_ptr -= 5; /* Back up to the length field */
ext_len = ip_ntohl(IP_GET_32ON8(len_ptr))-1-4;
/* Unpack msg */
if(ipssh_buffer_get_uint(pkt, &version))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_version :: failed to unpack msg");
return IPCOM_ERR_FAILED;
}
if(version != IPSSH_SFTP_VERSION)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_version :: unexpected version %d", version );
return IPCOM_ERR_FAILED;
}
channel->state = IPSSH_CH_STATE_CLT_SFTP;
/* Read/remove extension data */
while (ext_len>0)
{
char* ext_name;
char* ext_data;
Ip_bool ok = IP_FALSE;
ext_name = ipssh_buffer_get_sshstr(pkt);
ext_data = ipssh_buffer_get_sshstr(pkt);
if (ext_name && ext_data)
{
ext_len = pkt->end - pkt->offset;
ok = IP_TRUE;
}
if (ext_name)
ipcom_free(ext_name);
if (ext_data)
ipcom_free(ext_data);
if (!ok)
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_pkt_dispatch
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_sftp_clt_pkt_dispatch(Ipssh_channel* channel, Ipcom_buffer* pkt)
{
Ip_u8 pkt_type;
Ip_err rc;
Ip_u32 req_id;
if(ipcom_buffer_get_char(pkt, &pkt_type) != IPCOM_SUCCESS)
return IPCOM_ERR_FAILED;
IPCOM_LOG2(DEBUG2, "ipssh_sftp_clt_pkt_dispatch :: pkt type: %d, data_len:%d",
pkt_type, ipcom_buffer_len(pkt));
switch(pkt_type)
{
case IPSSH_FXP_VERSION:
rc = ipssh_msg_fxp_version(channel, pkt);
break;
/* Message an sftp client should never receive */
case IPSSH_FXP_CLOSE:
case IPSSH_FXP_OPENDIR:
case IPSSH_FXP_READDIR:
case IPSSH_FXP_OPEN:
default:
if(ipssh_buffer_get_uint(pkt, &req_id) == IPCOM_SUCCESS)
{
(void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_BAD_MESSAGE);
IPCOM_LOG3(ERR, "ipssh_sftp_clt_pkt_dispatch :: unknown pkt type: %d, ch_id: %d, req_id: %d",
pkt_type, channel->my_ch_id, req_id);
}
IP_PANIC2();
return IPCOM_ERR_FAILED;
}
return rc;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_sftp_clt_open
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_open(Ipssh_conn* ssh_conn, const char* hostname, Ip_u16 port)
{
Ip_err rc;
Ipcom_buffer* pkt;
Ipssh_channel* channel;
if(!ssh_conn || !hostname || !ssh_conn->data.clt.userid[0]
|| (!ssh_conn->data.clt.pw[0] && !ssh_conn->data.clt.key && !ssh_conn->data.clt.host_key)) /* One of pw, pubkey or hostbased auth data must be supplied */
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
ssh_conn->mode = IPSSH_MODE_CLT;
rc = ipssh_setup_clt_transport(ssh_conn, hostname, port);
if(rc != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open, ipssh_setup_clt_transport");
return rc;
}
ssh_conn->data.clt.channel = ipssh_channel_new(ssh_conn, 0xffffffff, ipssh_channel_data_sftp_handler);
if(!ssh_conn->data.clt.channel)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open :: ipssh_channel_new failed");
return IPCOM_ERR_FAILED;
}
channel = ssh_conn->data.clt.channel;
ipssh_channel_sftp_init(channel, IPSSH_CH_TYPE_SFTP_CLT);
channel->ssh_conn = ssh_conn;
pkt = ipssh_get_channel_open_pkt(channel, IPSSH_SESSION, 128);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open :: ipssh_get_channel_open_pkt failed");
return IPCOM_ERR_FAILED;
}
rc = ipssh_send_pkt(ssh_conn, pkt, IP_TRUE);
if(rc != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open :: ipssh_send_pkt failed");
return IPCOM_ERR_FAILED;
}
channel->state = IPSSH_CH_STATE_CH_OPEN_SENT;
while(channel->state != IPSSH_CH_STATE_CLT_SFTP)
{
switch(channel->state)
{
case IPSSH_CH_STATE_CH_OPEN_SENT:
case IPSSH_CH_STATE_CH_REQUEST_SENT:
case IPSSH_CH_STATE_CLT_FXP_INIT_SENT:
if(ipssh_read_and_dispatch_v2_pkt(ssh_conn, 0) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open :: ipssh_read_and_dispatch_v2_pkt failed");
return IPCOM_ERR_FAILED;
}
break;
case IPSSH_CH_STATE_OPENED:
if(ipssh_send_channel_request_subsystem(channel, 1, IPSSH_SFTP_STR) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open :: ipssh_send_channel_request_subsystem failed");
return IPCOM_ERR_FAILED;
}
break;
case IPSSH_CH_STATE_CH_REQUEST_OK:
if(ipssh_send_fxp_init(channel) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open :: ipssh_send_ftp_init failed");
return IPCOM_ERR_FAILED;
}
break;
default:
IP_PANIC();
}
}
channel->data.sftp.cwd = ipcom_malloc(IPSSH_SFTP_MAX_PATH);
if(!channel->data.sftp.cwd)
return IPCOM_ERR_FAILED;
if(ipssh_sftp_clt_get_realpath(ssh_conn, ".", channel->data.sftp.cwd, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_open :: ipssh_sftp_clt_get_realpath failed");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_get
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_get(Ipssh_conn* ssh_conn, const char* remote_path, const char* local_path)
{
Ip_err rc = IPCOM_ERR_FAILED;
Ip_u8 pkt_type;
char abs_remote_path[IPSSH_SFTP_MAX_PATH];
Ipssh_channel* channel;
Ip_u32 req_id;
Ip_u32 error;
Ip_u8* handle = IP_NULL;
Ip_u32 handle_len;
int fd;
Ipssh_sftp_func_cb* func;
Ip_u64 offset;
Ipcom_list pending_reqs;
Ipssh_sftp_clt_read_req* read_req;
Ip_u32 max_reqs;
Ip_u32 read_len = 0;
ipcom_list_init(&pending_reqs);
max_reqs = ipcom_sysvar_get_as_int("ipssh.sftp.clt_max_reqs", 4);
if (max_reqs < 1)
max_reqs = 1;
if (max_reqs > IPSSH_SFTP_CLT_MAX_REQS)
max_reqs = IPSSH_SFTP_CLT_MAX_REQS;
if(ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel
|| !local_path || !remote_path || ipcom_strlen(local_path) > IPSSH_SFTP_MAX_PATH
|| ipcom_strlen(remote_path) > IPSSH_SFTP_MAX_PATH)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
channel = ssh_conn->data.clt.channel;
func = ipssh_get_sftp_func_cb(local_path);
if(!func)
return IPCOM_ERR_FAILED;
if(ipssh_make_abs_path(channel->data.sftp.cwd, remote_path, abs_remote_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
/* Open local file */
fd = func->open_cb(local_path, IP_O_WRONLY|IP_O_CREAT|IP_O_TRUNC, 0666);
if(fd == -1)
{
IPCOM_LOG1(ERR, "ipssh_sftp_clt_get :: failed to open local file: %s", local_path);
return IPCOM_ERR_FAILED;
}
if(ipssh_send_fxp_open(channel, abs_remote_path, IPSSH_FXF_READ, 0, IP_NULL))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get :: ipssh_send_fxp_open() failed");
goto cleanup;
}
if(ipssh_read_sftp_msg(channel))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get :: ipssh_read_sftp_msg() failed");
goto cleanup;
}
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
switch(pkt_type)
{
case IPSSH_FXP_HANDLE:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| (handle = ipssh_buffer_get_sshbuf(ssh_conn->recv_plain_buf, &handle_len)) == IP_NULL)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get :: failed to unpack FXP_HANDLE msg");
goto cleanup;
}
break;
case IPSSH_FXP_STATUS:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &error))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
switch(error)
{
case IPSSH_FX_NO_SUCH_FILE:
rc = IPCOM_ERR_NOT_FOUND;
goto cleanup;
case IPSSH_FX_PERMISSION_DENIED:
rc = IPCOM_ERR_PERMISSION_DENIED;
goto cleanup;
default:
goto cleanup;
}
default:
IPCOM_LOG1(ERR, "ipssh_sftp_clt_get :: unexpected sftp msg received: %d", pkt_type);
goto cleanup;
}
IP_U64_CLEAR(offset);
/* File opened, start reading */
do
{
Ip_u32 data_len;
if (ipcom_list_length(&pending_reqs) == 0)
{
read_len = ipssh_get_sftp_req_len(channel, IPSSH_SRV2CLT, 0);
if (ipssh_sftp_clt_dispatch_read_req(channel,
&pending_reqs,
IP_NULL,
handle,
handle_len,
++channel->data.sftp.req_id,
0,
read_len) != IPCOM_SUCCESS)
{
IPCOM_LOG2(ERR, "ipssh_sftp_clt_get :: failed to dispatch initial read request: local path: %s, remote path: %s",
local_path, remote_path);
goto cleanup;
}
IP_U64_ADD32(offset, read_len);
}
if(ipssh_read_sftp_msg(channel))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get :: ipssh_read_sftp_msg() failed");
goto cleanup;
}
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
switch(pkt_type)
{
case IPSSH_FXP_DATA:
case IPSSH_FXP_STATUS:
if (ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id))
{
IPCOM_LOG1(ERR, "ipssh_sftp_clt_get :: failed to unpack req_id in %s msg",
pkt_type == IPSSH_FXP_DATA ? "FXP_DATA": "FXP_STATUS");
goto cleanup;
}
read_req = ipssh_sftp_clt_id2read_req(&pending_reqs, req_id);
if (!read_req)
{
IPCOM_LOG2(ERR, "ipssh_sftp_clt_get :: unexpected req_id (%d) received in %s msg",
req_id, pkt_type == IPSSH_FXP_DATA ? "FXP_DATA": "FXP_STATUS");
goto cleanup;
}
break;
default:
IPCOM_LOG1(ERR, "ipssh_sftp_clt_get :: unexpected sftp msg received: %d", pkt_type);
goto cleanup;
}
switch(pkt_type)
{
case IPSSH_FXP_DATA:
if (ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &data_len)
|| ipcom_buffer_len(ssh_conn->recv_plain_buf) != data_len)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get :: failed to unpack FXP_DATA msg");
goto cleanup;
}
if (data_len > read_req->len)
{
IPCOM_LOG2(WARNING, "ipssh_sftp_clt_get :: received more bytes (%d) than requested (%d)",
data_len, read_req->len);
data_len = read_req->len;
}
if (func->lseek_cb(fd, read_req->offset, IP_SEEK_SET) == -1)
{
IPCOM_LOG2(ERR, "ipssh_sftp_clt_get :: lseek_cb failed, offset: %llu, errno: %d",
(long long unsigned int)read_req->offset, ipcom_errno);
goto cleanup;
}
if (func->write_cb(fd, (char*)ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf), data_len) != (int)data_len)
{
IPCOM_LOG1(ERR, "ipssh_sftp_clt_get :: failed to write to file, errno:%d", ipcom_errno);
goto cleanup;
}
if (data_len == read_req->len)
{
/* The entire request was returned, dispatch a new request */
read_req->len = ipssh_get_sftp_req_len(channel, IPSSH_SRV2CLT, data_len);
read_req->req_id = ++channel->data.sftp.req_id;
IP_U64_COPY(read_req->offset, offset);
IP_U64_ADD32(offset, read_req->len);
if(ipssh_send_fxp_read(channel, handle, handle_len, read_req) != IPCOM_SUCCESS)
goto cleanup;
/* Dispatch another request? */
if (max_reqs > ipcom_list_length(&pending_reqs))
{
if (ipssh_sftp_clt_dispatch_read_req(channel,
&pending_reqs,
IP_NULL,
handle,
handle_len,
++channel->data.sftp.req_id,
offset,
read_len) != IPCOM_SUCCESS)
{
IPCOM_LOG3(ERR, "ipssh_sftp_clt_get :: failed to dispatch read request %d: local path: %s, remote path: %s",
ipcom_list_length(&pending_reqs), local_path, remote_path);
goto cleanup;
}
IP_U64_ADD32(offset, read_len);
}
}
else
{
/* Only a fraction of the request was returned, request the remaining part */
read_req->len -= data_len;
read_req->req_id = ++channel->data.sftp.req_id;
IP_U64_ADD32(read_req->offset, data_len);
if(ipssh_send_fxp_read(channel, handle, handle_len, read_req) != IPCOM_SUCCESS)
goto cleanup;
/* Lower the requested read size in case we are requesting larger 'chunks' than the server can deliver. */
if (data_len < read_len)
{
if (data_len >= 4096)
read_len = data_len;
else
read_len = 4096; /* Don't allow the read size to become unreasonable small */
}
}
(void)ipcom_buffer_consume(ssh_conn->recv_plain_buf, data_len);
break;
case IPSSH_FXP_STATUS:
if (ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &error))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get :: failed to unpack FXP_STATUS msg 2");
goto cleanup;
}
switch(error)
{
case IPSSH_FX_EOF:
ipcom_list_remove(&read_req->list);
ipcom_free(read_req);
break;
default:
(void)ipssh_send_fxp_close(channel, handle, handle_len);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
break;
default:
IP_PANIC(); /* We should never get here */
goto cleanup;
}
} while (ipcom_list_length(&pending_reqs) > 0);
rc = ipssh_send_fxp_close(channel, handle, handle_len);
cleanup:
for (read_req = (Ipssh_sftp_clt_read_req*)ipcom_list_first(&pending_reqs);
read_req;
read_req = (Ipssh_sftp_clt_read_req*)ipcom_list_first(&pending_reqs))
{
ipcom_list_remove(&read_req->list);
ipcom_free(read_req);
}
func->close_cb(fd); /*lint !e522*/
if(rc != IPCOM_SUCCESS)
func->unlink_cb(local_path); /*lint !e522*/
if(handle)
ipcom_free(handle);
return rc;
}
/*
*===========================================================================
* ipssh_sftp_clt_put_flags
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_put_flags(Ipssh_conn* ssh_conn, const char* local_path, const char* remote_path, Ip_u32 remote_path_flags)
{
Ip_err rc = IPCOM_ERR_FAILED;
char abs_remote_path[IPSSH_SFTP_MAX_PATH];
Ip_u8 pkt_type;
Ipssh_channel* channel;
Ip_u32 req_id;
Ip_u32 error;
Ip_u8* handle = IP_NULL;
Ip_u32 handle_len;
int fd;
Ipssh_sftp_func_cb* func;
Ip_u64 offset;
Ipcom_buffer* pkt = IP_NULL;
struct Ip_stat file_attr;
if(!ssh_conn || ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel
|| !local_path || !remote_path)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
channel = ssh_conn->data.clt.channel;
func = ipssh_get_sftp_func_cb(local_path);
if(!func)
return IPCOM_ERR_FAILED;
if(ipssh_make_abs_path(channel->data.sftp.cwd, remote_path, abs_remote_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
/* Open local file */
fd = func->open_cb(local_path, IP_O_RDONLY, 0);
if(fd == -1)
{
IPCOM_LOG1(ERR, "ipssh_sftp_clt_put_flags :: failed to open local file: %s", local_path);
return IPCOM_ERR_FAILED;
}
ipcom_memset(&file_attr, 0, sizeof(file_attr));
file_attr.st_mode = 0644;
if(ipssh_send_fxp_open(channel, abs_remote_path, remote_path_flags,
IPSSH_FILEXFER_ATTR_PERMISSIONS, &file_attr))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: ipssh_send_fxp_open() failed");
goto cleanup;
}
if(ipssh_read_sftp_msg(channel))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: ipssh_read_sftp_msg() failed");
goto cleanup;
}
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
switch(pkt_type)
{
case IPSSH_FXP_HANDLE:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| (handle = ipssh_buffer_get_sshbuf(ssh_conn->recv_plain_buf, &handle_len)) == IP_NULL)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: failed to unpack FXP_HANDLE msg");
goto cleanup;
}
break;
case IPSSH_FXP_STATUS:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &error))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
switch(error)
{
case IPSSH_FX_NO_SUCH_FILE:
rc = IPCOM_ERR_NOT_FOUND;
goto cleanup;
case IPSSH_FX_PERMISSION_DENIED:
rc = IPCOM_ERR_PERMISSION_DENIED;
goto cleanup;
default:
goto cleanup;
}
default:
IPCOM_LOG1(ERR, "ipssh_sftp_clt_put_flags :: unexpected sftp msg received: %d", pkt_type);
goto cleanup;
}
/* File opened, start writing */
IP_U64_CLEAR(offset);
while(1)
{
Ip_u32 req_len;
Ip_s32 bytes_read;
Ip_u8* len_ptr;
int min_win_size;
if(pkt)
ipcom_buffer_free(pkt);
/* pkt overhead: sftp msg len (4), sftp msg type (1), req_id (4), handle_len+4, offset (8), len 4 */
min_win_size = 4+1+4+handle_len+4+8+4;
min_win_size++; /* We must be able to send at least one byte of file content */
ipssh_sftp_clt_check_ch_win(channel, min_win_size);
req_len = ipssh_get_sftp_req_len(channel, IPSSH_CLT2SRV, 0);
req_len = IP_MIN(req_len, IPSSH_CH_MAX_PKT-5);
ip_assert(req_len > 0);
pkt = ipssh_buffer_new_sftp_pkt(req_len, IPSSH_FXP_WRITE);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: failed to allocate FXP_WRITE msg");
goto cleanup;
}
channel->data.sftp.req_id++;
if(ipssh_buffer_put_uint(pkt, channel->data.sftp.req_id)
|| ipssh_buffer_put_sshbuf(pkt, handle, handle_len)
|| ipssh_buffer_put_u64(pkt, offset))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: failed to pack msg");
goto cleanup;
}
len_ptr = ipcom_buffer_end_ptr(pkt);
if(ipssh_buffer_put_uint(pkt, 0)) /* Dummy length */
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: failed to pack msg 2");
goto cleanup;
}
ip_assert(ipcom_buffer_capacity(pkt) > IPSSH_MAX_PAD);
bytes_read = (Ip_s32)func->read_cb(fd, (char*)ipcom_buffer_end_ptr(pkt), ipcom_buffer_capacity(pkt)-IPSSH_MAX_PAD);
if(bytes_read < 0)
{
IPCOM_LOG3(ERR, "ipssh_sftp_clt_put_flags :: read_cb failed: offset hi:%d, lo:%d, req_len",
IP_U64_GETHI(offset), IP_U64_GETLO(offset), ipcom_buffer_capacity(pkt));
goto cleanup;
}
else if(bytes_read == 0)
{
rc = ipssh_send_fxp_close(channel, handle, handle_len);
goto cleanup;
}
else
{
(void)ipcom_buffer_decr_cap(pkt, (Ip_u32)bytes_read);
IP_U64_ADD32(offset, bytes_read);
IP_SET_32ON8(len_ptr, ip_htonl(bytes_read));
if(ipssh_send_fxp_msg(channel, pkt) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: ipssh_send_fxp_msg failed for FXP_WRITE msg");
goto cleanup;
}
}
if(ipssh_read_sftp_msg(channel))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: ipssh_read_sftp_msg() failed");
goto cleanup;
}
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
switch(pkt_type)
{
case IPSSH_FXP_STATUS:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &error))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_put_flags :: failed to unpack FXP_STATUS msg 2");
goto cleanup;
}
switch(error)
{
case IPSSH_FX_OK:
break;
default:
IPCOM_LOG1(ERR, "ipssh_sftp_clt_put_flags :: FXP_STATUS error=%d", error);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
break;
default:
IPCOM_LOG1(ERR, "ipssh_sftp_clt_put_flags :: unexpected sftp msg received: %d", pkt_type);
goto cleanup;
}
}
cleanup:
(void)func->close_cb(fd);
if(pkt)
ipcom_buffer_free(pkt);
if(handle)
ipcom_free(handle);
return rc;
}
/*
*===========================================================================
* ipssh_sftp_clt_put
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_put(Ipssh_conn* ssh_conn, const char* local_path, const char* remote_path)
{
return ipssh_sftp_clt_put_flags(ssh_conn, local_path, remote_path, IPSSH_FXF_WRITE|IPSSH_FXF_CREAT|IPSSH_FXF_TRUNC);
}
/*
*===========================================================================
* ipssh_sftp_clt_opendir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC void*
ipssh_sftp_clt_opendir(struct Ipssh_conn_st* ssh_conn, const char* path)
{
Ipssh_channel* channel;
char abs_path[IPSSH_SFTP_MAX_PATH];
Ipssh_sftp_clt_dir_handle* handle;
Ip_u32 req_id;
Ip_u32 status;
Ip_u8 pkt_type;
IPCOM_LOG1(DEBUG, "ipssh_sftp_clt_opendir :: path=%s", path);
if(!ssh_conn || ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED
|| !ssh_conn->data.clt.channel
|| !path || ipcom_strlen(path) > IPSSH_SFTP_MAX_PATH)
{
IP_PANIC2();
return IP_NULL;
}
channel = ssh_conn->data.clt.channel;
if(ipssh_make_abs_path(channel->data.sftp.cwd, path, abs_path, sizeof(abs_path)) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_opendir :: ipssh_make_abs_path failed");
return IP_NULL;
}
handle = ipcom_malloc(sizeof(Ipssh_sftp_clt_dir_handle));
if(!handle)
{
return IP_NULL;
}
ipcom_memset(handle, 0, sizeof(Ipssh_sftp_clt_dir_handle));
handle->magic = IPSSH_SFTP_CLT_HANDLE_MAGIC;
if(ipssh_send_fxp_opendir(channel, abs_path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_opendir :: ipssh_send_fxp_opendir failed");
goto cleanup;
}
if(ipssh_read_sftp_msg(channel))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_opendir :: ipssh_read_sftp_msg() failed");
goto cleanup;
}
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &pkt_type))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_opendir :: failed to unpack msg");
goto cleanup;
}
switch(pkt_type)
{
case IPSSH_FXP_HANDLE:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| (handle->ptr=ipssh_buffer_get_sshbuf(ssh_conn->recv_plain_buf, &handle->len)) == IP_NULL)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_opendir :: failed to unpack msg");
goto cleanup;
}
break;
case IPSSH_FXP_STATUS:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_opendir :: failed to unpack STATUS msg");
}
else
IPCOM_LOG1(ERR, "ipssh_sftp_clt_opendir :: FXP_REALPATH failed, status: %d", status);
goto cleanup;
default:
IP_PANIC();
goto cleanup;
}
return handle;
cleanup:
if(handle)
ipcom_free(handle);
return IP_NULL;
}
/*
*===========================================================================
* ipssh_sftp_clt_readdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ipssh_dirent*
ipssh_sftp_clt_readdir(struct Ipssh_conn_st* ssh_conn, void* handle)
{
Ipssh_channel* channel;
Ipssh_sftp_clt_dir_handle* dir_handle = (Ipssh_sftp_clt_dir_handle*) handle;
Ip_u32 req_id;
Ip_u32 status;
Ip_u32 attr_flags;
Ip_u8 pkt_type;
char* filename = IP_NULL;
char* longname = IP_NULL;
Ipssh_dirent* ret = IP_NULL;
if(!ssh_conn || ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED
|| !ssh_conn->data.clt.channel || !dir_handle || dir_handle->magic != IPSSH_SFTP_CLT_HANDLE_MAGIC)
{
IP_PANIC2();
return IP_NULL;
}
channel = ssh_conn->data.clt.channel;
ipcom_memset(&dir_handle->ret, 0, sizeof(dir_handle->ret));
if(dir_handle->count == 0)
{
if(ipssh_send_fxp_readdir(channel, dir_handle->ptr, dir_handle->len) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_readdir :: ipssh_send_fxp_readdir() failed");
return IP_NULL;
}
if(ipssh_read_sftp_msg(channel))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_readdir :: ipssh_read_sftp_msg() failed");
goto cleanup;
}
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &pkt_type))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_readdir :: failed to unpack msg");
goto cleanup;
}
switch(pkt_type)
{
case IPSSH_FXP_NAME:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &dir_handle->count))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_readdir :: failed to unpack FXP_NAME msg");
goto cleanup;
}
if(dir_handle->count == 0)
goto cleanup;
dir_handle->pkt = ssh_conn->recv_plain_buf;
ssh_conn->recv_plain_buf = IP_NULL;
break;
case IPSSH_FXP_STATUS:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_readdir :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
return IP_NULL;
default:
IP_PANIC();
return IP_NULL;
}
}
if((filename=ipssh_buffer_get_sshstr(dir_handle->pkt)) == IP_NULL
|| (longname=ipssh_buffer_get_sshstr(dir_handle->pkt)) == IP_NULL
|| ipssh_buffer_get_uint(dir_handle->pkt, &attr_flags) != IPCOM_SUCCESS
|| ipssh_unpack_attr(dir_handle->pkt, attr_flags, &dir_handle->ret.attr) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_readdir :: failed to unpack FXP_NAME msg");
dir_handle->count = 0;
goto cleanup;
}
dir_handle->count--;
ipcom_strncpy(dir_handle->ret.name, filename, sizeof(dir_handle->ret.name) - 1);
ipcom_strncpy(dir_handle->ret.longname, longname, sizeof(dir_handle->ret.longname) - 1);
ret = &dir_handle->ret;
cleanup:
if(dir_handle->count == 0)
{
if(dir_handle->pkt != IP_NULL)
{
ipcom_buffer_free(dir_handle->pkt);
dir_handle->pkt = IP_NULL;
}
}
if(filename)
ipcom_free(filename);
if(longname)
ipcom_free(longname);
return ret;
}
/*
*===========================================================================
* ipssh_sftp_clt_closedir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_closedir(struct Ipssh_conn_st* ssh_conn, void* handle)
{
Ip_err rc;
Ipssh_sftp_clt_dir_handle* dir_handle = (Ipssh_sftp_clt_dir_handle*) handle;
if(!ssh_conn || ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED
|| !ssh_conn->data.clt.channel || !dir_handle || dir_handle->magic != IPSSH_SFTP_CLT_HANDLE_MAGIC)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
rc = ipssh_send_fxp_close(ssh_conn->data.clt.channel, dir_handle->ptr, dir_handle->len);
ipcom_free(dir_handle->ptr);
if(dir_handle->pkt)
ipcom_buffer_free(dir_handle->pkt);
ipcom_free(dir_handle);
return rc;
}
/*
*===========================================================================
* ipssh_sftp_clt_close
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_close(Ipssh_conn* ssh_conn)
{
if(!ssh_conn)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
ipssh_conn_free(ssh_conn);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_get_cwd
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_get_cwd(Ipssh_conn* ssh_conn, char* buf, int buf_len)
{
if(ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel
|| !buf)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
if(buf_len > (int)ipcom_strlen(ssh_conn->data.clt.channel->data.sftp.cwd))
{
ipcom_strcpy(buf, ssh_conn->data.clt.channel->data.sftp.cwd);
return IPCOM_SUCCESS;
}
else
return IPCOM_ERR_INVALID_ARG;
}
/*
*===========================================================================
* ipssh_sftp_clt_get_realpath
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_get_realpath(Ipssh_conn* ssh_conn, const char* path, char* buf, int buf_len)
{
Ipssh_channel* channel;
Ip_u8 pkt_type;
Ip_u32 count;
Ip_u32 req_id;
Ip_u32 attr_flags;
char* filename = IP_NULL;
char* longname = IP_NULL;
struct Ip_stat file_attr;
Ip_u32 status;
Ip_err rc = IPCOM_ERR_FAILED;
if(!ssh_conn || ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
channel = ssh_conn->data.clt.channel;
if(ipssh_send_fxp_realpath(channel, path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get_realpath :: ipssh_send_fxp_realpath() failed");
return IPCOM_ERR_FAILED;
}
if(ipssh_read_sftp_msg(channel))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get_realpath :: ipssh_read_sftp_msg() failed");
goto cleanup;
}
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &pkt_type))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get_realpath :: failed to unpack msg");
goto cleanup;
}
switch(pkt_type)
{
case IPSSH_FXP_NAME:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &count)
|| (filename=ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) == IP_NULL
|| (longname=ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) == IP_NULL
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &attr_flags)
|| ipssh_unpack_attr(ssh_conn->recv_plain_buf, attr_flags, &file_attr))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get_realpath :: failed to unpack FXP_NAME msg");
goto cleanup;
}
if(buf_len < (int)ipcom_strlen(filename))
{
rc = IPCOM_ERR_INVALID_ARG;
goto cleanup;
}
ipcom_strcpy(buf, filename);
break;
case IPSSH_FXP_STATUS:
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_get_realpath :: failed to unpack STATUS msg");
}
else
IPCOM_LOG1(ERR, "ipssh_sftp_clt_get_realpath :: FXP_REALPATH failed, status: %d", status);
goto cleanup;
default:
IPCOM_LOG1(ERR, "ipssh_sftp_clt_get_realpath :: unexpected sftp msg received, %d", pkt_type);
IP_PANIC();
goto cleanup;
}
rc = IPCOM_SUCCESS;
cleanup:
if(filename)
ipcom_free(filename);
if(longname)
ipcom_free(longname);
return rc;
}
/*
*===========================================================================
* ipssh_sftp_clt_mkdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_mkdir(struct Ipssh_conn_st* ssh_conn, const char* path, Ip_mode_t mode)
{
Ipssh_channel* channel;
char abs_path[IPSSH_SFTP_MAX_PATH];
Ip_u8 pkt_type;
Ip_u32 req_id;
Ip_u32 attr_flags;
struct Ip_stat file_attr;
Ip_u32 status;
Ipcom_buffer *pkt;
Ip_err rc = IPCOM_ERR_FAILED;
if(!ssh_conn || ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel || !path)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
channel = ssh_conn->data.clt.channel;
if(ipssh_make_abs_path(channel->data.sftp.cwd, path, abs_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
pkt = ipssh_buffer_new_sftp_pkt(32+ipcom_strlen(abs_path), IPSSH_FXP_MKDIR);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_mkdir :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshstr(pkt, abs_path);
ipcom_memset(&file_attr, 0, sizeof(file_attr));
file_attr.st_mode = mode;
attr_flags = IPSSH_FILEXFER_ATTR_PERMISSIONS;
(void)ipssh_pack_attr(pkt, &file_attr, attr_flags);
if(ipssh_send_fxp_msg(channel, pkt))
goto cleanup;
/* Read reply */
if(ipssh_read_sftp_msg(channel))
goto cleanup;
if(ipcom_buffer_get_char(channel->ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
if(pkt_type == IPSSH_FXP_STATUS)
{
if(ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_unlink :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
switch(status)
{
case IPSSH_FX_OK:
rc = IPCOM_SUCCESS;
break;
case IPSSH_FX_PERMISSION_DENIED:
rc = IPCOM_ERR_PERMISSION_DENIED;
break;
}
}
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_sftp_clt_rmdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_rmdir(struct Ipssh_conn_st* ssh_conn, const char* path)
{
Ipssh_channel* channel;
char abs_path[IPSSH_SFTP_MAX_PATH];
Ip_u8 pkt_type;
Ip_u32 req_id;
Ip_u32 status;
Ipcom_buffer *pkt;
Ip_err rc = IPCOM_ERR_FAILED;
if(!ssh_conn || ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel || !path)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
channel = ssh_conn->data.clt.channel;
if(ipssh_make_abs_path(channel->data.sftp.cwd, path, abs_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
pkt = ipssh_buffer_new_sftp_pkt(32+ipcom_strlen(abs_path), IPSSH_FXP_RMDIR);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_rmdir :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshstr(pkt, abs_path);
if(ipssh_send_fxp_msg(channel, pkt))
goto cleanup;
/* Read reply */
if(ipssh_read_sftp_msg(channel))
goto cleanup;
if(ipcom_buffer_get_char(channel->ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
if(pkt_type == IPSSH_FXP_STATUS)
{
if(ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_unlink :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
switch(status)
{
case IPSSH_FX_OK:
rc = IPCOM_SUCCESS;
break;
case IPSSH_FX_PERMISSION_DENIED:
rc = IPCOM_ERR_PERMISSION_DENIED;
break;
}
}
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_sftp_clt_chdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_chdir(struct Ipssh_conn_st* ssh_conn, const char* path)
{
char abs_path[IPSSH_SFTP_MAX_PATH];
if(ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel || !path)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
if(ipssh_make_abs_path(ssh_conn->data.clt.channel->data.sftp.cwd, path, abs_path, sizeof(abs_path)) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
if(ipssh_sftp_clt_get_realpath(ssh_conn, abs_path, abs_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_chdir :: ipssh_sftp_clt_get_realpath() failed");
return IPCOM_ERR_FAILED;
}
ipcom_strcpy(ssh_conn->data.clt.channel->data.sftp.cwd, abs_path);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_unlink
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_unlink(struct Ipssh_conn_st* ssh_conn, const char* path)
{
Ip_err rc = IPCOM_ERR_FAILED;
char abs_path[IPSSH_SFTP_MAX_PATH];
Ipcom_buffer* pkt;
Ip_u8 pkt_type;
Ip_u32 req_id;
Ip_u32 status;
Ipssh_channel* channel;
if(ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel || !path)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
channel = ssh_conn->data.clt.channel;
if(ipssh_make_abs_path(channel->data.sftp.cwd, path, abs_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
pkt = ipssh_buffer_new_sftp_pkt(32+ipcom_strlen(abs_path), IPSSH_FXP_REMOVE);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_remove :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshstr(pkt, abs_path);
if(ipssh_send_fxp_msg(channel, pkt))
goto cleanup;
/* Read reply */
if(ipssh_read_sftp_msg(channel))
goto cleanup;
if(ipcom_buffer_get_char(channel->ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
if(pkt_type == IPSSH_FXP_STATUS)
{
if(ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_unlink :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
switch(status)
{
case IPSSH_FX_OK:
rc = IPCOM_SUCCESS;
break;
case IPSSH_FX_NO_SUCH_FILE:
rc = IPCOM_ERR_NOT_FOUND;
break;
case IPSSH_FX_PERMISSION_DENIED:
rc = IPCOM_ERR_PERMISSION_DENIED;
break;
}
}
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_sftp_clt_set_key
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_set_key(struct Ipssh_conn_st* ssh_conn, EVP_PKEY* key)
{
if(!ssh_conn || !key || (key->type != NID_dsa && key->type != NID_rsaEncryption))
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
ssh_conn->data.clt.key = key;
ssh_conn->data.clt.key->references++;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_set_hostb_key
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_set_hostb_key(struct Ipssh_conn_st* ssh_conn, EVP_PKEY* key)
{
if(!ssh_conn || !key || (key->type != NID_dsa && key->type != NID_rsaEncryption))
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
ssh_conn->data.clt.host_key = key;
ssh_conn->data.clt.host_key->references++;
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_set_userid
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_set_userid(Ipssh_conn* ssh_conn, const char* user)
{
if(!ssh_conn || !user || ipcom_strlen(user)>sizeof(ssh_conn->data.clt.userid))
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
ipcom_strcpy(ssh_conn->data.clt.userid, user);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_set_userid_pw
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_set_pw(Ipssh_conn* ssh_conn, const char* pw)
{
if(!ssh_conn || !pw || ipcom_strlen(pw)>sizeof(ssh_conn->data.clt.pw))
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
ipcom_strcpy(ssh_conn->data.clt.pw, pw);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_sftp_clt_rename
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_err
ipssh_sftp_clt_rename(struct Ipssh_conn_st* ssh_conn, const char* oldpath, const char* newpath)
{
Ip_err rc = IPCOM_ERR_FAILED;
char old_abs_path[IPSSH_SFTP_MAX_PATH];
char new_abs_path[IPSSH_SFTP_MAX_PATH];
Ipcom_buffer* pkt;
Ip_u8 pkt_type;
Ip_u32 req_id;
Ip_u32 status;
Ipssh_channel* channel;
if(ssh_conn->conn_state != IPSSH_CONN_STATE_CLT_CONNECTED || !ssh_conn->data.clt.channel || !oldpath || !newpath)
{
IP_PANIC2();
return IPCOM_ERR_INVALID_ARG;
}
channel = ssh_conn->data.clt.channel;
if(ipssh_make_abs_path(channel->data.sftp.cwd, oldpath, old_abs_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
if(ipssh_make_abs_path(channel->data.sftp.cwd, newpath, new_abs_path, IPSSH_SFTP_MAX_PATH) != IPCOM_SUCCESS)
return IPCOM_ERR_INVALID_ARG;
pkt = ipssh_buffer_new_sftp_pkt(32+ipcom_strlen(old_abs_path)+ipcom_strlen(new_abs_path), IPSSH_FXP_RENAME);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_rename :: out of memory");
return IPCOM_ERR_FAILED;
}
channel->data.sftp.req_id++;
(void)ipssh_buffer_put_int(pkt, channel->data.sftp.req_id);
(void)ipssh_buffer_put_sshstr(pkt, old_abs_path);
(void)ipssh_buffer_put_sshstr(pkt, new_abs_path);
if(ipssh_send_fxp_msg(channel, pkt))
goto cleanup;
/* Read reply */
if(ipssh_read_sftp_msg(channel))
goto cleanup;
if(ipcom_buffer_get_char(channel->ssh_conn->recv_plain_buf, &pkt_type))
goto cleanup;
if(pkt_type == IPSSH_FXP_STATUS)
{
if(ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &req_id)
|| ipssh_buffer_get_uint(channel->ssh_conn->recv_plain_buf, &status))
{
IPCOM_LOG0(ERR, "ipssh_sftp_clt_rename :: failed to unpack FXP_STATUS msg");
goto cleanup;
}
switch(status)
{
case IPSSH_FX_OK:
rc = IPCOM_SUCCESS;
break;
case IPSSH_FX_NO_SUCH_FILE:
rc = IPCOM_ERR_NOT_FOUND;
break;
case IPSSH_FX_PERMISSION_DENIED:
rc = IPCOM_ERR_PERMISSION_DENIED;
break;
}
}
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
#else
int ipssh_sftp_clt_empty_file;
#endif /* IPSSH_USE_SFTP_CLT */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/