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

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