/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 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 #include #include #include #include #include #include #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 **************************************************************************** */