1517 lines
45 KiB
C
1517 lines
45 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_channel.c,v $ $Revision: 1.29.10.8 $
|
|
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_channel.c,v $
|
|
* $Author: lwang7 $
|
|
* $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
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipssh_h.h"
|
|
#include "ipssh.h"
|
|
#include "ipssh_buffer.h"
|
|
#include "ipssh_transport.h"
|
|
#include "ipssh_connection.h"
|
|
#ifdef IPSSH_USE_SFTP
|
|
#include "ipssh_sftp.h"
|
|
#endif
|
|
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
typedef Ip_err (*Ipssh_ch_open_handler)(Ipssh_channel* channel, Ipcom_buffer* pkt);
|
|
|
|
struct Ipssh_ch_open_handler_map_st
|
|
{
|
|
const char* channel_type;
|
|
Ipssh_ch_open_handler handler;
|
|
};
|
|
typedef struct Ipssh_ch_open_handler_map_st Ipssh_ch_open_handler_map;
|
|
|
|
struct Ipssh_subsystem_init_cb_entry_st
|
|
{
|
|
Ipcom_list list;
|
|
char* subsystem_name;
|
|
Ipssh_subsystem_init_cb init_cb;
|
|
};
|
|
typedef struct Ipssh_subsystem_init_cb_entry_st Ipssh_subsystem_init_cb_entry;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_STATIC Ip_err ipssh_open_session_channel(Ipssh_channel* channel, Ipcom_buffer* pkt);
|
|
IP_STATIC Ip_err ipssh_open_fwd_tcpip_channel(Ipssh_channel* channel, Ipcom_buffer* pkt);
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
Ipssh_ch_open_handler_map ipssh_open_handler_map[] =
|
|
{
|
|
{IPSSH_SESSION, ipssh_open_session_channel},
|
|
{IPSSH_DIRECT_TCPIP, ipssh_open_fwd_tcpip_channel},
|
|
{IP_NULL, IP_NULL}
|
|
};
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_channel_request_subsystem(Ipssh_channel* channel, Ip_u8 want_reply, const char* subsystem)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
IPCOM_LOG1(DEBUG, "ipssh_send_channel_request_subsystem :: ch_id:%d", channel->peer_ch_id);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32+ipcom_strlen(subsystem), IPSSH_MSG_CHANNEL_REQUEST);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_request_subsystem :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
if(ipssh_buffer_put_int(pkt, channel->peer_ch_id)
|
|
|| ipssh_buffer_put_sshstr(pkt, IPSSH_SUBSYSTEM_STR)
|
|
|| ipcom_buffer_put_char(pkt, want_reply)
|
|
|| ipssh_buffer_put_sshstr(pkt, subsystem))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_request_subsystem :: out of memory");
|
|
ipcom_buffer_free(pkt);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
else
|
|
{
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_request_subsystem :: ipssh_send_pkt");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
channel->state = IPSSH_CH_STATE_CH_REQUEST_SENT;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_channel_data_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_channel_data_msg(Ipssh_channel* channel, Ipcom_buffer* pkt)
|
|
{
|
|
int msg_len;
|
|
|
|
msg_len = (int)ipcom_buffer_len(pkt)-14;
|
|
|
|
*ipcom_buffer_ptr2pos(pkt, 5) = IPSSH_MSG_CHANNEL_DATA;
|
|
IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(pkt, 6), channel->peer_ch_id); /*lint !e666*/
|
|
IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(pkt, 10), msg_len); /*lint !e666*/
|
|
|
|
channel->peer_win -= msg_len;
|
|
|
|
return ipssh_send_pkt(channel->ssh_conn, pkt, IP_FALSE);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_channel_win_adjust_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_channel_win_adjust_msg(Ipssh_channel* channel, Ip_u32 size_change)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
/* IPCOM_LOG2(DEBUG, "ipssh_send_channel_win_adjust_msg :: ch_id:%d, size change:%d", channel->peer_ch_id, size_change); */
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_CHANNEL_WINDOW_ADJUST);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_win_adjust_msg :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(size_change));
|
|
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_win_adjust_msg :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_ch_open_conf_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_ch_open_conf_msg(Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
IPCOM_LOG6(DEBUG, "ipssh_send_ch_open_conf_msg :: my ch id:%d, peer ch id:%d, "
|
|
"my win size:%d, peer win size:%d, my max pkt size:%d, peer max pkt size:%d",
|
|
channel->my_ch_id, channel->peer_ch_id, channel->my_win, channel->peer_win,
|
|
channel->my_max_pkt_size, channel->peer_max_pkt_size);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32,IPSSH_MSG_CHANNEL_OPEN_CONFIRMATION);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_ch_open_conf_msg :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->my_ch_id));
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->my_win));
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->my_max_pkt_size));
|
|
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_ch_open_conf_msg :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_ch_open_fail_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_ch_open_fail_msg(Ipssh_conn* ssh_conn, Ip_u32 peer_ch_id, Ip_u32 reason_code, const char* reason_str)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
IPCOM_LOG2(DEBUG, "ipssh_send_ch_fail_msg :: peer ch id:%d, reason code: %d", peer_ch_id, reason_code);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(64 + ipcom_strlen(reason_str), IPSSH_MSG_CHANNEL_OPEN_FAILURE);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_ch_open_fail_msg :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(peer_ch_id));
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(reason_code));
|
|
(void)ipssh_buffer_put_sshstr(pkt, reason_str);
|
|
(void)ipssh_buffer_put_sshstr(pkt, "en");
|
|
|
|
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_ch_open_conf_msg :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_channel_open
|
|
*===========================================================================
|
|
* Description: This function creates an MSG_CHANNEL_OPEN message and adds
|
|
* the generic part of the message.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipcom_buffer*
|
|
ipssh_get_channel_open_pkt(Ipssh_channel* channel, const char* ch_type, int size)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
ip_assert(size > (int)(6 + ipcom_strlen(ch_type)+4 +3*4)); /*lint !e574*/
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(size, IPSSH_MSG_CHANNEL_OPEN);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_get_channel_open_pkt :: out of memory");
|
|
return IP_NULL;
|
|
}
|
|
|
|
/* Open channel to convey forwarded tcp connection */
|
|
if(ipssh_buffer_put_sshstr(pkt, ch_type) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(channel->my_ch_id)) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(channel->my_win)) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(channel->my_max_pkt_size)))
|
|
{
|
|
ipcom_buffer_free(pkt);
|
|
return IP_NULL;
|
|
}
|
|
|
|
return pkt;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_success
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_success(Ipssh_conn* ssh_conn)
|
|
{
|
|
Ip_u32 ch_id;
|
|
Ipssh_channel* channel;
|
|
/* Unpack message */
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) || ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_success :: failed to unpack msg");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
IPCOM_LOG1(DEBUG, "ipssh_msg_channel_success :: ch_id: %d", ch_id);
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_success :: unknown ch id: %d", ch_id);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
channel->state = IPSSH_CH_STATE_CH_REQUEST_OK;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_channel_success
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_channel_success(Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
IPCOM_LOG1(DEBUG, "ipssh_send_channel_success :: channel=%d", channel->peer_ch_id);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_CHANNEL_SUCCESS);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_success :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_success :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_channel_failure
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_channel_failure(Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
IPCOM_LOG1(DEBUG, "ipssh_send_channel_failure :: channel=%d", channel->peer_ch_id);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_CHANNEL_FAILURE);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_failure :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_failure :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_channel_exit(Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_CHANNEL_REQUEST);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_failure :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
(void)ipssh_buffer_put_sshstr(pkt, IPSSH_EXIT_STATUS);
|
|
(void)ipcom_buffer_put_char(pkt,0);
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->data.shell.exit_status_val));
|
|
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_exit :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_msg_channel_eof
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_msg_channel_eof(Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
IPCOM_LOG2(DEBUG, "ipssh_send_msg_channel_eof :: clt ch id: %d, srv ch id: %d",
|
|
channel->peer_ch_id, channel->my_ch_id);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_CHANNEL_EOF);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_msg_channel_eof :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_msg_channel_eof :: ipssh_send_pkt() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_eof
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_eof(struct Ipssh_conn_st* ssh_conn)
|
|
{
|
|
Ip_u32 ch_id;
|
|
Ipssh_channel* channel;
|
|
|
|
/* Unpack message */
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) || ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_eof :: failed to unpack msg");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
IPCOM_LOG1(DEBUG, "ipssh_msg_channel_eof :: ch_id: %d", ch_id);
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_eof :: unknown ch id: %d", ch_id);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
channel->eof_rec = 1;
|
|
if (channel->type == IPSSH_CH_TYPE_SHELL)
|
|
{
|
|
/* Shell channels require special handling in case so called shell here documents are used.
|
|
Then we must wait until all data that has been output from the shell has been sent
|
|
to the ssh client, before we send a channel EOF msg. */
|
|
if(channel->data.shell.shell_fd != IP_INVALID_SOCKET)
|
|
(void) ipcom_shutdown(channel->data.shell.shell_fd, IP_SHUT_WR);
|
|
}
|
|
else
|
|
{
|
|
if(!channel->eof_sent)
|
|
{
|
|
if(ipssh_send_msg_channel_eof(channel) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
channel->eof_sent =1;
|
|
}
|
|
if(ipssh_send_channel_close_msg(channel) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
else
|
|
channel->close_sent = 1;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_channel_close_msg
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_channel_close_msg(Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
IPCOM_LOG2(DEBUG, "ipssh_send_channel_close_msg :: clt ch id: %d, srv ch id: %d",
|
|
channel->peer_ch_id, channel->my_ch_id);
|
|
ip_assert(!channel->close_sent);
|
|
|
|
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_CHANNEL_CLOSE);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_close_msg :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
if(ipssh_send_pkt(channel->ssh_conn, pkt, IP_TRUE))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_channel_close_msg :: ipssh_send_pkt failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
channel->close_sent = 1;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_close
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_close(struct Ipssh_conn_st* ssh_conn)
|
|
{
|
|
Ip_u32 ch_id;
|
|
Ipssh_channel* channel;
|
|
|
|
/* Unpack message */
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) || ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_close :: failed to unpack message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
IPCOM_LOG1(DEBUG, "ipssh_msg_channel_close :: ch_id: %d", ch_id);
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_close :: unknown ch id: %d", ch_id);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
if(channel->close_rec)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_close :: close msg already received, ch id: %d", ch_id);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
channel->close_rec = 1;
|
|
if(!channel->close_sent)
|
|
{
|
|
if(ipssh_send_channel_close_msg(channel))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_close :: failed to send close msg");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
ipssh_channel_close(channel);
|
|
|
|
if(ssh_conn->ch_map.size == 0)
|
|
return IPCOM_ERR_FAILED;
|
|
else
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_data
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_data(struct Ipssh_conn_st* ssh_conn)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ip_u32 ch_id;
|
|
Ip_s32 rc = IPCOM_SUCCESS;
|
|
Ip_u32 data_len;
|
|
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) || ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_data :: failed to unpack msg");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_data :: failed to lookup channel data");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
data_len = IPSSH_GET_32_PTR(ipcom_buffer_start_ptr(ssh_conn->recv_plain_buf)); /*lint !e666*/
|
|
|
|
/* IPCOM_LOG2(DEBUG, "ipssh_msg_channel_data :: channel id: %d, len: %d", ch_id, data_len); */
|
|
|
|
if(!channel->handler)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_data :: channel un-initialized, releasing connection");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(channel->handler(channel, ssh_conn->recv_plain_buf))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_data :: data handler failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipssh_send_channel_win_adjust_msg(channel, data_len) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_data :: ipssh_send_channel_win_adjust_msg() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipcom_buffer_len(ssh_conn->recv_plain_buf) == 0)
|
|
ipcom_buffer_reset(ssh_conn->recv_plain_buf);
|
|
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_channel_new
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipssh_channel*
|
|
ipssh_channel_new(Ipssh_conn* ssh_conn, Ip_u32 peer_ch_id, Ipssh_ch_data_handler handler)
|
|
{
|
|
|
|
Ipssh_channel *ret;
|
|
Ip_u32 my_ch_id;
|
|
Ip_u32 ch_win_size;
|
|
|
|
my_ch_id = ipssh_channel_new_id(&ssh_conn->ch_map);
|
|
|
|
ch_win_size = ipcom_sysvar_get_as_int("ipssh.channel.win_size", IPSSH_CH_WIN_SIZE);
|
|
|
|
ret = ipssh_channel_map_insert(ssh_conn,
|
|
my_ch_id,
|
|
peer_ch_id,
|
|
ch_win_size,
|
|
0xffffffff,
|
|
IPSSH_CH_MAX_PKT,
|
|
0xffffffff,
|
|
handler);
|
|
if (ret)
|
|
ssh_conn->no_channels++;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_channel_map_insert
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipssh_channel*
|
|
ipssh_channel_map_insert(Ipssh_conn* ssh_conn,
|
|
Ip_u32 my_ch_id,
|
|
Ip_u32 peer_ch_id,
|
|
Ip_u32 my_win,
|
|
Ip_u32 peer_win,
|
|
Ip_u32 my_max_pkt_size,
|
|
Ip_u32 peer_max_pkt_size,
|
|
Ipssh_ch_data_handler handler)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ipcom_list* list = (Ipcom_list*) &ssh_conn->ch_map;
|
|
|
|
IPCOM_LOG6(DEBUG2, "ipssh_channel_map_insert :: my_ch_id=%d, peer_ch_id=%d, my_win=%d,"
|
|
"peer_win=%d, my_max_pkt=%d, peer_max_pkt=%d",
|
|
my_ch_id, peer_ch_id, my_win, peer_win, my_max_pkt_size, peer_max_pkt_size);
|
|
|
|
channel = ipcom_malloc(sizeof(Ipssh_channel));
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_channel_map_insert :: out of memory");
|
|
return IP_NULL;
|
|
}
|
|
ipcom_memset(channel, 0, sizeof(Ipssh_channel));
|
|
|
|
channel->state = IPSSH_CH_STATE_CH_INIT;
|
|
channel->data.port_fwd.sock = IP_INVALID_SOCKET;
|
|
channel->my_ch_id = my_ch_id;
|
|
channel->peer_ch_id = peer_ch_id;
|
|
channel->my_win = my_win;
|
|
channel->peer_win = peer_win;
|
|
channel->my_max_pkt_size = my_max_pkt_size;
|
|
channel->peer_max_pkt_size = peer_max_pkt_size;
|
|
channel->handler = handler;
|
|
channel->ssh_conn = ssh_conn;
|
|
|
|
ipcom_list_insert_last(list, (Ipcom_list*) channel);
|
|
|
|
return channel;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_sock2channel
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipssh_channel*
|
|
ipssh_sock2channel(Ipssh_channel_map* ch_map, Ip_fd sock)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ipcom_list* list = (Ipcom_list*) ch_map;
|
|
|
|
IPCOM_LOG1(DEBUG2, "ipssh_sock2channel :: sock=%d", sock);
|
|
|
|
for(channel = (Ipssh_channel*) ipcom_list_first(list);
|
|
channel;
|
|
channel = (Ipssh_channel*) ipcom_list_next((Ipcom_list*)channel))
|
|
{
|
|
if(channel->data.port_fwd.sock == sock)
|
|
return channel;
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_channel_id2ch
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipssh_channel*
|
|
ipssh_channel_id2ch(Ipssh_channel_map* ch_map, Ip_u32 ch_id)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ipcom_list* list = (Ipcom_list*) ch_map;
|
|
|
|
IPCOM_LOG1(DEBUG2, "ipssh_channel_id2ch :: ch_id=%d", ch_id);
|
|
|
|
for(channel = (Ipssh_channel*) ipcom_list_first(list);
|
|
channel;
|
|
channel = (Ipssh_channel*) ipcom_list_next((Ipcom_list*)channel))
|
|
{
|
|
if(channel->my_ch_id == ch_id)
|
|
return channel;
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_channel_map_lookup
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipssh_channel*
|
|
ipssh_channel_map_lookup(Ipssh_channel_map* ch_map, Ip_fd shell_fd)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ipcom_list* list = (Ipcom_list*) ch_map;
|
|
|
|
IPCOM_LOG1(DEBUG2, "ipssh_channel_map_lookup :: fd=%d", shell_fd);
|
|
|
|
for(channel = (Ipssh_channel*) ipcom_list_first(list);
|
|
channel;
|
|
channel = (Ipssh_channel*) ipcom_list_next((Ipcom_list*)channel))
|
|
{
|
|
if(channel->data.shell.shell_fd == shell_fd)
|
|
return channel;
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_open
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_open(Ipssh_conn* ssh_conn)
|
|
{
|
|
char* channel_type = IP_NULL;
|
|
Ip_u32 peer_ch_id;
|
|
Ip_u32 win_size;
|
|
Ip_u32 max_pkt_size;
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
Ipssh_ch_open_handler_map* handler_map;
|
|
int i;
|
|
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) ||
|
|
!(channel_type = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &peer_ch_id) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &win_size) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &max_pkt_size))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_open :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
IPCOM_LOG4(DEBUG, "ipssh_msg_channel_open :: ch type: %s, peer ch id: %d, win size: %d, max pkt: %d",
|
|
channel_type, peer_ch_id, win_size, max_pkt_size);
|
|
|
|
if(ssh_conn->no_channels >= ssh_conn->config.max_channels)
|
|
{
|
|
ipssh_send_ch_open_fail_msg(ssh_conn, peer_ch_id, IPSSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "Maximum number of open channels reached");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Call handler function for this channel type, store channel data, and send open confirmation message */
|
|
for(i=0,handler_map = ipssh_open_handler_map; handler_map[i].channel_type != IP_NULL; i++)
|
|
{
|
|
if(ipcom_strcmp(handler_map[i].channel_type, channel_type) == 0)
|
|
{
|
|
Ipssh_channel* channel;
|
|
channel = ipssh_channel_new(ssh_conn, peer_ch_id, IP_NULL);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_open :: ipssh_channel_map_insert() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
channel->peer_max_pkt_size = max_pkt_size;
|
|
channel->peer_win = win_size;
|
|
if(handler_map[i].handler(channel, ssh_conn->recv_plain_buf) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_open :: handler failed for channel type: %s", channel_type);
|
|
ipssh_channel_close(channel);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_open :: unknown channel type: %s", channel_type);
|
|
rc = IPCOM_ERR_FAILED;
|
|
|
|
(void)ipssh_send_ch_open_fail_msg(ssh_conn, peer_ch_id, IPSSH_OPEN_UNKNOWN_CHANNEL_TYPE, "");
|
|
|
|
cleanup:
|
|
if(channel_type)
|
|
ipcom_free(channel_type);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_open_session_channel
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_open_session_channel(Ipssh_channel* channel, Ipcom_buffer* pkt)
|
|
{
|
|
(void) pkt; /* Entire packet already unpacked */
|
|
|
|
if(ipssh_send_ch_open_conf_msg(channel) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_open_session_channel :: ipssh_send_ch_open_conf_msg() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_open_fwd_tcpip_channel
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_open_fwd_tcpip_channel(Ipssh_channel* channel, Ipcom_buffer* pkt)
|
|
{
|
|
char* dst_addr;
|
|
Ip_u32 dst_port;
|
|
char* src_addr_str = IP_NULL;
|
|
Ip_u32 src_port;
|
|
Ip_s32 rc = IPCOM_SUCCESS;
|
|
|
|
if(!(dst_addr = ipssh_buffer_get_sshstr(pkt)) ||
|
|
ipssh_buffer_get_uint(pkt, &dst_port) ||
|
|
!(src_addr_str = ipssh_buffer_get_sshstr(pkt)) ||
|
|
ipssh_buffer_get_uint(pkt, &src_port))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_open_fwd_tcpip_channel :: failed to unpack remaining message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
channel->type = IPSSH_CH_TYPE_PORT_FWD;
|
|
|
|
if(!channel->ssh_conn->config.port_fwd)
|
|
{
|
|
if(ipssh_send_ch_open_fail_msg(channel->ssh_conn, channel->peer_ch_id, IPSSH_OPEN_CONNECT_FAILED,
|
|
"Port forwarding not turned on") != IPCOM_SUCCESS)
|
|
rc = IPCOM_ERR_FAILED;
|
|
ipssh_channel_close(channel);
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipssh_open_tcp_conn(dst_addr, dst_port, &channel->data.port_fwd.sock) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR,"ipssh_open_fwd_tcpip_channel :: ipssh_open_tcp_conn failed");
|
|
if(ipssh_send_ch_open_fail_msg(channel->ssh_conn, channel->peer_ch_id, IPSSH_OPEN_CONNECT_FAILED,
|
|
"Invalid destination address") != IPCOM_SUCCESS)
|
|
rc = IPCOM_ERR_FAILED;
|
|
ipssh_channel_close(channel);
|
|
goto cleanup;
|
|
}
|
|
|
|
channel->handler = ipssh_channel2sock_handler;
|
|
|
|
if(channel->ssh_conn->port_fwd_cb)
|
|
{
|
|
if(channel->ssh_conn->port_fwd_cb(channel->ssh_conn,
|
|
channel->data.port_fwd.sock,
|
|
IPSSH_PORT_FWD_EVENT_OPEN))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_open_fwd_tcpip_channel :: ipssh_send_ch_open_conf_msg() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
if(ipssh_send_ch_open_conf_msg(channel) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_open_fwd_tcpip_channel :: ipssh_send_ch_open_conf_msg() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
cleanup:
|
|
if(dst_addr)
|
|
ipcom_free(dst_addr);
|
|
if(src_addr_str)
|
|
ipcom_free(src_addr_str);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_channel_close
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
/* TODO: change return type to Ip_err? */
|
|
IP_GLOBAL void
|
|
ipssh_channel_close(Ipssh_channel* channel)
|
|
{
|
|
Ipssh_ch_data_entry* ch_data_entry;
|
|
|
|
IPCOM_LOG2(DEBUG2, "ipssh_channel_close :: clt ch id: %d, srv ch id: %d",
|
|
channel->peer_ch_id, channel->my_ch_id);
|
|
|
|
ipcom_list_remove((Ipcom_list*)channel);
|
|
|
|
channel->ssh_conn->no_channels--;
|
|
ip_assert(channel->ssh_conn->no_channels>=0);
|
|
|
|
switch(channel->type)
|
|
{
|
|
case IPSSH_CH_TYPE_SHELL:
|
|
if(channel->data.shell.shell_fd != IP_INVALID_SOCKET)
|
|
(void) ipcom_shutdown(channel->data.shell.shell_fd, IP_SHUT_WR);
|
|
break;
|
|
|
|
case IPSSH_CH_TYPE_PORT_FWD:
|
|
if(channel->data.port_fwd.sock != IP_INVALID_SOCKET)
|
|
{
|
|
if(channel->ssh_conn->port_fwd_cb)
|
|
{
|
|
channel->ssh_conn->port_fwd_cb(channel->ssh_conn,
|
|
channel->data.port_fwd.sock,
|
|
IPSSH_PORT_FWD_EVENT_CLOSE); /*lint !e522*/
|
|
}
|
|
(void)ipcom_socketclose(channel->data.port_fwd.sock);
|
|
}
|
|
break;
|
|
case IPSSH_CH_TYPE_SUBSYSTEM:
|
|
for (ch_data_entry = IPCOM_LIST_FIRST(&channel->ch_data_list);
|
|
ch_data_entry;
|
|
ch_data_entry = IPCOM_LIST_FIRST(&channel->ch_data_list))
|
|
{
|
|
ipcom_list_remove(&ch_data_entry->list);
|
|
if (ch_data_entry->msg)
|
|
ipcom_buffer_free(ch_data_entry->msg);
|
|
ipcom_free(ch_data_entry);
|
|
}
|
|
break;
|
|
#ifdef IPSSH_USE_SFTP_CLT
|
|
case IPSSH_CH_TYPE_SFTP_CLT:
|
|
ipcom_free(channel->data.sftp.cwd);
|
|
/* this is an intentional fall through to the next case */
|
|
#endif
|
|
#ifdef IPSSH_USE_SFTP
|
|
case IPSSH_CH_TYPE_SFTP_SRV: /*lint !e616*/
|
|
ipssh_sftp_cleanup(channel);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
ipcom_free(channel);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_ch_win_adjust
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_ch_win_adjust(Ipssh_conn* ssh_conn)
|
|
{
|
|
Ip_u32 ch_id, size;
|
|
Ipssh_channel* channel;
|
|
|
|
/* Unpack message */
|
|
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, &size))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_ch_win_adjust :: failed to unpack message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_ch_win_adjust :: failed to look up channel data");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
channel->peer_win += size;
|
|
/* IPCOM_LOG3(DEBUG, "ipssh_msg_ch_win_adjust :: ch id:%d, size: %d, new window: %d", */
|
|
/* ch_id, size, channel->peer_win); */
|
|
|
|
/* If the send window size goes down to 0 (or close to 0) for a channel, we stop
|
|
* reading data, pending a channel window adjust message. Now we have a send
|
|
* window so we can start reading from the connection again */
|
|
if(channel->type == IPSSH_CH_TYPE_PORT_FWD && channel->peer_win > 0
|
|
&& channel->data.port_fwd.sock != IP_INVALID_SOCKET && ssh_conn->port_fwd_cb)
|
|
{
|
|
return ssh_conn->port_fwd_cb(ssh_conn, channel->data.port_fwd.sock, IPSSH_PORT_FWD_EVENT_SEND_WINDOW_OPEN);
|
|
}
|
|
else if(IP_BIT_ISSET(channel->flags, IPSSH_CH_FLAG_WAITING_FOR_WIN_ADJUST_MSG)
|
|
&& (Ip_fd)channel->data.shell.shell_fd != IP_INVALID_SOCKET)
|
|
{
|
|
Ipssh_conn_ctx* ctx = (Ipssh_conn_ctx*)ssh_conn->extra;
|
|
if(IP_NULL != ctx)
|
|
{
|
|
IP_FD_SET((Ip_fd)channel->data.shell.shell_fd, &(ctx->read_set));
|
|
}
|
|
IP_BIT_CLR(channel->flags, IPSSH_CH_FLAG_WAITING_FOR_WIN_ADJUST_MSG);
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_open_confirmation
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_open_confirmation(Ipssh_conn* ssh_conn)
|
|
{
|
|
Ip_u32 peer_ch_id, my_ch_id;
|
|
Ip_u32 win_size;
|
|
Ip_u32 max_pkt_size;
|
|
Ipssh_channel* channel;
|
|
|
|
/* Unpack message */
|
|
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &my_ch_id) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &peer_ch_id) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &win_size) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &max_pkt_size))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_msg_channel_open_confirmation :: failed to unpack message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, my_ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_msg_channel_open_confirmation :: unknown channel id: %d", my_ch_id);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
channel->peer_max_pkt_size = max_pkt_size;
|
|
channel->peer_win = win_size;
|
|
channel->peer_ch_id = peer_ch_id;
|
|
|
|
channel->state = IPSSH_CH_STATE_OPENED;
|
|
|
|
if(channel->type == IPSSH_CH_TYPE_PORT_FWD && ssh_conn->port_fwd_cb)
|
|
if(ssh_conn->port_fwd_cb(ssh_conn, channel->data.port_fwd.sock, IPSSH_PORT_FWD_EVENT_OPEN) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_msg_channel_open_failure
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_msg_channel_open_failure(Ipssh_conn* ssh_conn)
|
|
{
|
|
Ip_u32 my_ch_id;
|
|
Ip_u32 reason_code;
|
|
char* info = IP_NULL;
|
|
char* lang_tag = IP_NULL;
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
Ipssh_channel* channel = IP_NULL;
|
|
|
|
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &my_ch_id) ||
|
|
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &reason_code) ||
|
|
!(info = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
|
|
!(lang_tag = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)))
|
|
{
|
|
IPCOM_LOG0(ERR,"ipssh_msg_channel_open_failure :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, my_ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(WARNING,"ipssh_msg_channel_open_failure :: invalid ch id received: %d", my_ch_id);
|
|
goto cleanup;
|
|
}
|
|
|
|
channel->state = IPSSH_CH_STATE_ERR;
|
|
|
|
cleanup:
|
|
if(info)
|
|
ipcom_free(info);
|
|
if(lang_tag)
|
|
ipcom_free(lang_tag);
|
|
|
|
if(channel)
|
|
channel->state = IPSSH_CH_STATE_ERR;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_channel_new_id
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u32
|
|
ipssh_channel_new_id(Ipssh_channel_map* map)
|
|
{
|
|
Ip_u32 max_id = 0;
|
|
Ipssh_channel* channel;
|
|
|
|
for(channel = (Ipssh_channel*)ipcom_list_first((Ipcom_list*)map);
|
|
channel;
|
|
channel = (Ipssh_channel*)ipcom_list_next((Ipcom_list*)channel))
|
|
{
|
|
if(channel->my_ch_id != 0xffffffff && channel->my_ch_id > max_id)
|
|
max_id = channel->my_ch_id;
|
|
}
|
|
return max_id + 1;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_channel_init_subsystem
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_channel_init_subsystem(const char* subsystem_name,
|
|
Ipssh_channel* channel,
|
|
Ip_fd* sock)
|
|
{
|
|
Ipssh_subsystem_init_cb_entry* init_cb_entry;
|
|
|
|
for (init_cb_entry = IPCOM_LIST_FIRST(&g_ipssh.subsystem_init_cb_list);
|
|
init_cb_entry;
|
|
init_cb_entry = IPCOM_LIST_NEXT(&init_cb_entry->list))
|
|
{
|
|
if (ipcom_strcmp(subsystem_name, init_cb_entry->subsystem_name) == 0)
|
|
{
|
|
return init_cb_entry->init_cb(channel, sock);
|
|
}
|
|
}
|
|
|
|
return IPCOM_ERR_NOT_FOUND;
|
|
}
|
|
|
|
|
|
#ifdef IPTESTENGINE
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_test_subsystem_init_cb
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_test_subsystem_init_cb(Ipssh_channel* channel, Ip_fd* sock)
|
|
{
|
|
union Ip_sockaddr_union sockaddr;
|
|
char userid[32];
|
|
int user_len = sizeof(userid);
|
|
|
|
ipcom_memset(&sockaddr, 0, sizeof(sockaddr));
|
|
|
|
ipssh_peer_addr_get(channel, &sockaddr);
|
|
|
|
ipssh_user_get(channel, userid, &user_len);
|
|
|
|
return ipcom_start_shell(sock, IP_NULL);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_register_subsystem
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipssh_register_subsystem(const char* subsystem_name, Ipssh_subsystem_init_cb init_cb)
|
|
{
|
|
Ipssh_subsystem_init_cb_entry* init_cb_entry;
|
|
|
|
ipcom_mutex_lock(g_ipssh.code_mutex);
|
|
for (init_cb_entry = IPCOM_LIST_FIRST(&g_ipssh.subsystem_init_cb_list);
|
|
init_cb_entry;
|
|
init_cb_entry = IPCOM_LIST_NEXT(&init_cb_entry->list))
|
|
{
|
|
if (ipcom_strcmp(subsystem_name, init_cb_entry->subsystem_name) == 0)
|
|
{
|
|
ipcom_mutex_unlock(g_ipssh.code_mutex);
|
|
return IPCOM_ERR_ALREADY_INSTALLED;
|
|
}
|
|
}
|
|
|
|
init_cb_entry = ipcom_calloc(1, sizeof(Ipssh_subsystem_init_cb_entry));
|
|
if (!init_cb_entry)
|
|
{
|
|
ipcom_mutex_unlock(g_ipssh.code_mutex);
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
|
|
init_cb_entry->subsystem_name = ipcom_strdup(subsystem_name);
|
|
if (!init_cb_entry->subsystem_name)
|
|
{
|
|
ipcom_free(init_cb_entry);
|
|
ipcom_mutex_unlock(g_ipssh.code_mutex);
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
init_cb_entry->init_cb = init_cb;
|
|
ipcom_list_insert_last(&g_ipssh.subsystem_init_cb_list, &init_cb_entry->list);
|
|
ipcom_mutex_unlock(g_ipssh.code_mutex);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_deregister_subsystem
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipssh_deregister_subsystem(const char* subsystem_name)
|
|
{
|
|
Ipssh_subsystem_init_cb_entry* init_cb_entry;
|
|
|
|
ipcom_mutex_lock(g_ipssh.code_mutex);
|
|
|
|
for (init_cb_entry = IPCOM_LIST_FIRST(&g_ipssh.subsystem_init_cb_list);
|
|
init_cb_entry;
|
|
init_cb_entry = IPCOM_LIST_NEXT(&init_cb_entry->list))
|
|
{
|
|
if (ipcom_strcmp(subsystem_name, init_cb_entry->subsystem_name) == 0)
|
|
{
|
|
ipcom_list_remove(&init_cb_entry->list);
|
|
ipcom_mutex_unlock(g_ipssh.code_mutex);
|
|
ipcom_free(init_cb_entry->subsystem_name);
|
|
ipcom_free(init_cb_entry);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
ipcom_mutex_unlock(g_ipssh.code_mutex);
|
|
return IPCOM_ERR_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_peer_addr_get
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipssh_peer_addr_get(const struct Ipssh_channel_st* channel, union Ip_sockaddr_union* peer_addr)
|
|
{
|
|
Ip_fd peer_fd;
|
|
Ip_socklen_t socklen = sizeof(union Ip_sockaddr_union);
|
|
|
|
peer_fd = channel->ssh_conn->sock;
|
|
|
|
if (ipcom_getpeername(peer_fd, &peer_addr->sa, &socklen))
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_user_get
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipssh_user_get(const struct Ipssh_channel_st* channel, char* userid, int* userid_len)
|
|
{
|
|
const char* user;
|
|
int org_userid_len = *userid_len;
|
|
|
|
user = channel->ssh_conn->user;
|
|
if (!user)
|
|
{
|
|
userid[0] = 0;
|
|
*userid_len = 0;
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
*userid_len = ipcom_strlen(user);
|
|
if (ipcom_strlen(user)+1 > org_userid_len)
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
ipcom_strcpy(userid, user);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|