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

1471 lines
46 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_connection.c,v $ $Revision: 1.116.10.16 $
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_connection.c,v $
* $Author: svc-cmnet $ $Date: 2018-09-27 23:32:54 $
* $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_transport.h"
#ifdef IPSSH_USE_V1
#include "ipssh_transport_v1.h"
#include "ipssh_connection_v1.h"
#endif
#include "ipssh_auth.h"
#include "ipssh_buffer.h"
#include "ipssh_connection.h"
#include "ipssh_sftp.h"
#include <ipcom_clib.h>
#include <ipcom_config.h>
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_buffer.h>
#include <ipcom_sock.h>
#include <ipcom_sock2.h>
#include <ipcom_err.h>
#include <openssl/bn.h>
#include <openssl/hmac.h>
/*
****************************************************************************
* 4 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 5 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 6 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC Ip_err ipssh_start_shell(Ipssh_conn* ssh_conn, Ipssh_channel* channel);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
Ipssh_terminal_resize_cb ipssh_terminal_resize_cb = ipssh_set_win_size;
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
IP_GLOBAL Ip_err
ipssh_handle_exec_cmd(Ipssh_conn* ssh_conn, Ipssh_channel* channel, const char* exec_cmd)
{
Ip_size_t bytes_written = 0;
Ip_ssize_t bytes_read;
char buf[32];
Ip_fd shell_fd;
Ip_fd_set read_set;
struct Ip_timeval timeout;
int i;
if(ipssh_start_shell(ssh_conn, channel) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_handle_exec_cmd :: ipssh_start_shell() failed");
return IPCOM_ERR_FAILED;
}
shell_fd = channel->data.shell.shell_fd;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
/* wait for 3 second at most. */
for (i = 0; i < 3; i++)
{
IP_FD_ZERO(&read_set);
IP_FD_SET(shell_fd, &read_set);
if (ipcom_socketselect(shell_fd + 1, &read_set, IP_NULL, IP_NULL, &timeout) > 0)
break;
}
/* read the shell data and ignore the CR and blanks. */
if (IP_FD_ISSET(shell_fd, &read_set))
{
bytes_read = ipcom_socketread(channel->data.shell.shell_fd, buf, 32);
if(bytes_read == 0 || (bytes_read < 0 && (ipcom_errno == IP_ERRNO_ESHUTDOWN || ipcom_errno == IP_ERRNO_ECONNRESET)))
{
(void)ipssh_shell_exit2(ssh_conn, shell_fd);
return IPCOM_ERR_FAILED;
}
if(bytes_read < 0)
{
if(!(ipcom_errno == IP_ERRNO_EWOULDBLOCK
#if (defined(IP_PORT_OSE) || defined(IP_PORT_OSE5)) && (defined(IP_TARGET_SOFT) || defined(softOSE) || defined(IP_TARGET_SOLARIS2) || defined(IP_TARGET_LINUX) || defined(IP_TARGET_WIN32))
/* When INET=api in userconf file errno is not set properly. This is a somewhat crude work-around */
|| ipcom_errno == 0
#endif
))
return IPCOM_ERR_FAILED;
}
/* ignore the CR and blanks. */
}
ipcom_millisleep(10);
/* Set shell type to be IPSSH_CH_SHELL_EXEC_CMD and default return value to be */
channel->data.shell.shell_type = IPSSH_SHELL_EXEC_CMD;
channel->data.shell.exit_status_val = IPSSH_SHELL_EXEC_CMD_DEF_STATUS;
while (bytes_written < ipcom_strlen(exec_cmd))
{
int write_len;
write_len = ipcom_socketwrite(shell_fd, exec_cmd, ipcom_strlen(exec_cmd));
if (write_len < 0)
{
IPCOM_LOG1(ERR, "ipssh_handle_exec_cmd :: ipcom_socketwrite() failed, errno=%d", ipcom_errno);
return IPCOM_ERR_FAILED;
}
bytes_written += write_len;
if (bytes_written < ipcom_strlen(exec_cmd))
ipcom_millisleep(10);
}
if (ipcom_socketwrite(shell_fd, "\r", 1) != 1)
return IPCOM_ERR_FAILED;
if (ipcom_shutdown(shell_fd, IP_SHUT_WR) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_handle_exec_cmd :: ipcom_shutdown() failed, errno=%d", ipcom_errno);
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_handle_term_modes
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipssh_handle_term_modes(Ip_u8* buf, int len, Ipssh_term_modes *term_modes)
{
int i;
for(i=0; i<len; i++)
{
if (term_modes->ICRNL)
if (buf[i] == '\r')
buf[i] = '\n';
if (term_modes->IXBSDEL)
if (buf[i] == 0x7f)
buf[i] = 0x08;
}
}
/*
*===========================================================================
* ipssh_port_fwd_listen_handler
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_port_fwd_listen_handler(Ip_fd sock, Ipssh_conn_ctx* ctx, Ipssh_map_entry* map_entry)
{
Ip_fd clt_sock;
struct Ip_sockaddr_in clt_addr;
struct Ip_sockaddr_in srv_addr;
Ip_u32 len = sizeof(struct Ip_sockaddr_in);
Ip_u32 clt_port, srv_port;
Ipssh_channel* channel;
Ipcom_buffer* pkt;
(void) map_entry;
clt_sock = ipcom_accept(sock, (struct Ip_sockaddr*)&clt_addr, &len);
if(clt_sock == IP_INVALID_SOCKET)
{
IPCOM_LOG1(ERR, "ipssh_port_fwd_listen_handler :: ipcom_accept() failed, errno=%d", ipcom_errno);
return IPCOM_SUCCESS;
}
len = sizeof(struct Ip_sockaddr_in);
if(ipcom_getsockname(sock, (struct Ip_sockaddr*)&srv_addr, &len))
{
IPCOM_LOG1(ERR, "ipssh_port_fwd_listen_handler :: ipcom_getsockname() failed, errno=%d", ipcom_errno);
ipcom_socketclose(clt_sock);
return IPCOM_SUCCESS;
}
channel = ipssh_channel_new(ctx->ssh_conn, 0xffffffff, ipssh_channel2sock_handler);
if(channel == IP_NULL)
{
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: out of memory");
ipcom_socketclose(clt_sock);
return IPCOM_ERR_FAILED;
}
channel->data.port_fwd.sock = clt_sock;
channel->type = IPSSH_CH_TYPE_PORT_FWD;
srv_port = ip_ntohs(srv_addr.sin_port);
clt_port = ip_ntohs(clt_addr.sin_port);
pkt = ipssh_buffer_new_v2_pkt(128, IPSSH_MSG_CHANNEL_OPEN);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: out of memory");
return IPCOM_ERR_FAILED;
}
/* Open channel to convey forwarded tcp connection */
if(ipssh_buffer_put_sshstr(pkt, IPSSH_FORWARDED_TCPIP) ||
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)) ||
ipssh_buffer_put_sshstr(pkt, ipcom_inet_ntoa(srv_addr.sin_addr)) ||
ipcom_buffer_put_int(pkt, ip_htonl(srv_port)) ||
ipssh_buffer_put_sshstr(pkt, ipcom_inet_ntoa(clt_addr.sin_addr)) ||
ipcom_buffer_put_int(pkt, ip_htonl(clt_port)))
{
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: failed to pack message");
ipcom_buffer_free(pkt);
return IPCOM_ERR_FAILED;
}
if(ipssh_send_pkt(ctx->ssh_conn, pkt, IP_TRUE) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler :: failed to send message");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_port_fwd_handler_s2c
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_port_fwd_handler_s2c(Ip_fd sock, Ipssh_conn_ctx* ctx, Ipssh_map_entry* map_entry)
{
Ipssh_channel* channel;
Ipcom_buffer* pkt;
Ip_s32 rc = IPCOM_SUCCESS;
Ip_u32 req_bytes;
Ip_ssize_t read_bytes;
channel = ipssh_sock2channel(&ctx->ssh_conn->ch_map, sock);
if(!channel)
{
IPCOM_LOG1(ERR, "ipssh_port_fwd_handler_s2c :: no channel data for socket: %d", sock);
return IPCOM_ERR_FAILED;
}
if(channel->peer_win == 0)
{
/* Wait for a channel window adjust message to arrive */
IP_FD_CLR(sock, &(ctx->read_set));
return IPCOM_SUCCESS;
}
req_bytes = IP_MIN((Ip_u32)channel->peer_win,channel->peer_max_pkt_size);
pkt = ipssh_buffer_new_channel_data_pkt(req_bytes, channel);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_port_fwd_handler_s2c :: out of memory");
return IPCOM_ERR_FAILED;
}
read_bytes = ipcom_socketread(sock, ipcom_buffer_end_ptr(pkt), req_bytes);
if(read_bytes < 0)
{
IPCOM_LOG1(INFO, "ipssh_port_fwd_handler_s2c :: connection lost, errno=%d", ipcom_errno);
}
if(read_bytes < 0 || /* Port fwd connection lost */
read_bytes == 0) /* Port fwd connection closed */
{
ipcom_list_remove((Ipcom_list*)map_entry);
ipcom_free(map_entry);
IP_FD_CLR(sock, &(ctx->read_set));
if(!channel->close_sent)
{
if(ipssh_send_channel_close_msg(channel))
{
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
if(channel->close_rec)
ipssh_channel_close(channel);
if(ctx->ssh_conn->ch_map.size == 0)
rc = IPCOM_ERR_FAILED; /* Last channel closed, close SSH connection */
}
else /* Forward data */
{
(void)ipcom_buffer_decr_cap(pkt, read_bytes);
if(ipssh_send_channel_data_msg(channel, pkt) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_port_fwd_handler_s2c :: ipssh_send_channel_data_msg() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
cleanup:
if(pkt)
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_store_char
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_s32
ipssh_store_char(Ipcom_buffer* buf, Ipssh_term_modes* term_modes, Ip_u8 chr, Ip_u8* last_chr)
{
Ip_s32 ret=1;
switch(chr)
{
case '\n':
if(!term_modes->ONLCR)
break;
if(*last_chr == '\r')
break;
(void)ipcom_buffer_put_char(buf, '\r');
ret++;
break;
default:
break; /* NOP */
}
*last_chr = chr;
(void)ipcom_buffer_put_char(buf, chr);
return ret;
}
#ifdef IPSSH_ECHO_STDIN
/*
*===========================================================================
* ipssh_store_echo_char
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_s32
ipssh_store_echo_char(Ipcom_buffer* buf, Ipssh_term_modes* term_modes, Ip_u8 chr, Ip_u8* last_chr)
{
Ip_s32 ret=1;
(void) term_modes;
switch(chr)
{
case '\r':
(void)ipcom_buffer_put_char(buf, '\n');
break;
default:
break; /* NOP */
}
*last_chr = chr;
(void)ipcom_buffer_put_char(buf, chr);
return ret;
}
#endif
/*
*===========================================================================
* ipssh_parse_shell_return_value
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_parse_shell_return_value(Ipssh_channel* channel, char *data, Ip_ssize_t len)
{
int ret_val = 0;
char *tmp = data;
char *valuestr = IP_NULL;
/* add '\0' to the end of data. */
tmp[len] = '\0';
/* search the last match string "value = ". */
while((tmp = ipcom_strstr(tmp, "value = ")) != IP_NULL)
{
valuestr = tmp;
tmp += ipcom_strlen("value = ");
}
if(valuestr != IP_NULL)
{
valuestr += ipcom_strlen("value = ");
ret_val = ipcom_atoi(valuestr);
/* re-check whether the found string is output from shell in value = * = 0x*. */
while(ipcom_isdigit(*valuestr) != 0)
valuestr++;
if(ipcom_strncmp(valuestr, " = 0x", ipcom_strlen(" = 0x")) == 0)
{
if(ret_val == (int)ipcom_strtol(valuestr + ipcom_strlen(" = 0x"), IP_NULL, 16))
{
channel->data.shell.exit_status_val = ret_val;
return IPCOM_SUCCESS;
}
}
}
return IPCOM_ERR_FAILED;
}
/*
*===========================================================================
* ipssh_parse_terminal_modes
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_parse_terminal_modes(Ipssh_term_modes* term_modes, Ip_u8* term_mode, Ip_u32 len)
{
Ip_u32 pos = 0;
Ip_u32 value;
Ip_u8 mode;
if(!term_modes)
return IPCOM_ERR_FAILED;
while(pos < len)
{
mode = term_mode[pos];
if (mode == IPSSH_TTY_OP_END)
{
IPCOM_LOG2(DEBUG,"ipssh_parse_terminal_modes :: found end mark at pos %d, len=%d", pos, len);
return IPCOM_SUCCESS;
}
pos++;
if (len - pos < 4)
{
IPCOM_LOG1(DEBUG2,"ipssh_parse_terminal_modes :: illegal mode string, len=%d", len);
return IPCOM_ERR_FAILED;
}
value = IPSSH_GET_32_PTR(&(term_mode[pos]));
IPCOM_LOG2(DEBUG2,"ipssh_parse_terminal_modes :: [%d]:%d", mode, value);
switch(mode)
{
case IPSSH_ONLCR:
term_modes->ONLCR = (Ip_u8) value;
break;
case IPSSH_ICRNL:
term_modes->ICRNL = (Ip_u8) value;
break;
default:
break; /* NOP */
}
pos += 4;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_send_shell_stdout_data
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC int
ipssh_send_shell_stdout_data2(Ipssh_conn* ssh_conn, Ip_fd shell_fd, const char* data, Ip_ssize_t len)
{
int pos = 0;
int msg_size;
Ipssh_channel* channel;
Ipcom_buffer* pkt = IP_NULL;
#ifdef IPSSH_USE_V1
Ip_size_t len_pos = 0;
#endif
IPCOM_LOG2(DEBUG2, "ipssh_send_shell_stdout_data :: shell_fd: %d, len: %d", shell_fd, len);
if(!(channel = ipssh_channel_map_lookup(&ssh_conn->ch_map, shell_fd)))
{
IPCOM_LOG1(ERR, "ipssh_send_shell_stdout_data :: no channel data found for shell_fd=%d", shell_fd);
return IPCOM_ERR_FAILED;
}
if(channel->data.shell.shell_type == IPSSH_SHELL_EXEC_CMD)
{
/* Firstly try to find a return value in data. */
if(ipssh_parse_shell_return_value(channel, (char *)data, len) != IPCOM_SUCCESS)
{
int index;
char *tmp = channel->data.shell.return_value;
Ip_size_t buflen = ipcom_strlen(channel->data.shell.return_value);
/* The return value string may locate in two parts. So save the first one. The max string
is 31(That is value = 4294967295 = 0xFFFFFFFF). If current buflen>32, move the end part
of buf to the deginning. */
if(buflen > 31)
{
for(index = 31; index < buflen; index++)
tmp[index - 31] = tmp[index];
tmp[index - 31] = '\0';
}
(void)ipcom_strncat(tmp, data, 31);
/* try to search again in the buffer. */
(void)ipssh_parse_shell_return_value(channel, tmp, ipcom_strlen(tmp));
}
}
while(pos < len)
{
if(ssh_conn->ssh_version == 2)
{
msg_size = (int)len+10;
if(msg_size > channel->peer_win)
msg_size = channel->peer_win;
if(msg_size > (int)channel->peer_max_pkt_size)
msg_size = channel->peer_max_pkt_size;
if(msg_size <= 2) /* The channel send window is too small to send any more data */
break;
pkt = ipssh_buffer_new_channel_data_pkt(msg_size, channel);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: out of memory 2");
return IPCOM_ERR_FAILED;
}
}
#ifdef IPSSH_USE_V1
else
{
pkt = ipssh_buffer_new_v1_pkt(1024, IPSSH_SMSG_V1_STDOUT_DATA);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: out of memory 1");
return IPCOM_ERR_FAILED;
}
len_pos = ipcom_buffer_len(pkt);
(void)ipcom_buffer_decr_cap(pkt, 4);
}
#endif
while(pos < len && ipcom_buffer_capacity(pkt) > (2+IPSSH_MAX_PAD)) /* ipssh_store_char may store 2 characters */
{
(void)ipssh_store_char(pkt, &channel->data.shell.term_modes, data[pos], &channel->data.shell.last_chr);
pos++;
}
if(ssh_conn->ssh_version == 2)
{
if(ipssh_send_channel_data_msg(channel, pkt) != IPCOM_SUCCESS)
{
ipcom_buffer_free(pkt);
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: ipssh_send_channel_data_msg() failed");
return IPCOM_ERR_FAILED;
}
ipcom_buffer_free(pkt);
}
#ifdef IPSSH_USE_V1
else
{
int pkt_len;
char* ptr;
pkt_len = (int)(ipcom_buffer_len(pkt) - len_pos - 4);
ptr = ipcom_buffer_ptr2pos(pkt, len_pos);
IPSSH_SET_32_PTR(ptr, pkt_len);
if(ipssh_send_v1_pkt(ssh_conn, pkt) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_send_shell_stdout_data :: failed to send STDOUT_DATA pkt");
return IPCOM_ERR_FAILED;
}
}
#endif
if(pos == len)
break;
}
if(pos == 0)
{
ipcom_errno = IP_ERRNO_EWOULDBLOCK;
return IPCOM_ERR_FAILED;
}
else
return pos;
}
/*
*===========================================================================
* ipssh_shell_stdin_handler
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_shell_stdin_handler(Ipssh_channel* channel, Ipcom_buffer* msg)
{
Ip_u32 len;
if(ipssh_buffer_get_uint(msg, &len) || ipcom_buffer_len(msg) < len)
{
IPCOM_LOG0(ERR, "ipssh_shell_stdin_handler :: failed to unpack msg");
return IPCOM_ERR_FAILED;
}
IPCOM_LOG2(DEBUG2, "ipssh_shell_stdin_handler :: ch_id=%d, len=%d", channel->peer_ch_id, len);
ipssh_handle_term_modes(ipcom_buffer_start_ptr(msg), len, &(channel->data.shell.term_modes));
if(ipssh_sock_shell_stdin(channel->data.shell.shell_fd, (const char*)ipcom_buffer_start_ptr(msg), len) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR, "ipssh_shell_stdin_handler :: ipssh.shell_stdin_cb() failed, len=%d", len);
return IPCOM_ERR_FAILED;
}
#ifdef IPSSH_ECHO_STDIN
{
Ipcom_buffer* echo_pkt;
Ip_u8 last_chr = 0;
Ip_u32 pos = 0;
echo_pkt = ipssh_buffer_new_channel_data_pkt(len+1, channel);
while(pos < len)
{
Ip_u8 chr;
ipcom_buffer_get_char(msg, &chr);
(void)ipssh_store_echo_char(echo_pkt, &(channel->data.shell.term_modes), chr, &last_chr);
pos++;
if(pos == len
|| *ipcom_buffer_ptr2pos(msg, ipcom_buffer_len(msg)-1) == '\r'
|| *ipcom_buffer_ptr2pos(msg, ipcom_buffer_len(msg)-1) == '\n'
|| ipcom_buffer_capacity(msg)<(IPSSH_MAX_PAD+2))
{
if(ipssh_send_channel_data_msg(ctx, channel, echo_pkt) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_shell_stdin_handler :: failed to echo data");
ipcom_buffer_free(echo_pkt);
return IPCOM_ERR_FAILED;
}
/* Reset buffer for CHANNEL_DATA_MSG re-use */
echo_pkt->offset = 0;
echo_pkt->end = 14;
}
}
ipcom_buffer_free(echo_pkt);
}
#else
ipcom_buffer_reset(msg);
#endif
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_start_shell
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_start_shell(Ipssh_conn* ssh_conn, Ipssh_channel* channel)
{
Ip_err rc = IPCOM_SUCCESS;
if (ipssh_shell_check_access_cb(ssh_conn->user, 0) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_start_shell :: ipssh_shell_check_access_cb() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
channel->handler = ipssh_shell_stdin_handler;
channel->data.shell.shell_fd = ipssh_sock_shell_start(ssh_conn, ssh_conn->user, ssh_conn->sh_ctx);
if(channel->data.shell.shell_fd == IP_INVALID_SOCKET)
{
IPCOM_LOG0(ERR, "ipssh_start_shell :: ipssh_sock_shell_start() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
cleanup:
/* No cleaning up needed */
return rc;
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_set_default_term_modes
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipssh_set_default_term_modes(Ipssh_term_modes* term_modes)
{
term_modes->ONLCR = 1;
#if defined(IP_PORT_VXWORKS) && IPCOM_USE_SHELL == IPCOM_SHELL_NATIVE
term_modes->ICRNL = 1;
term_modes->IXBSDEL = 1;
#endif
}
/*
*===========================================================================
* ipssh_open_tcp_conn
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_open_tcp_conn(const char* addr_str, Ip_u32 port, Ip_fd* sock)
{
union Ip_sockaddr_union addr;
struct Ip_addrinfo *res;
Ip_err rc = IPCOM_ERR_FAILED;
*sock = IP_INVALID_SOCKET;
ipcom_memset(&addr, 0, sizeof(addr));
if(ipcom_getaddrinfo(addr_str, IP_NULL, IP_NULL, &res) != 0)
{
IPCOM_LOG1(ERR,"ipssh_open_tcp_conn :: invalid addr: %s", addr_str);
return IPCOM_ERR_FAILED;
}
ipcom_memcpy(&addr.sa, res->ai_addr, res->ai_addrlen);
addr.sin.sin_port = (Ip_in_port_t)ip_htons(port); /* Port field is at the same offset for both INET and INET6 */
*sock = ipcom_socket(res->ai_family, IP_SOCK_STREAM, 0);
if(*sock == IP_INVALID_SOCKET)
{
IPCOM_LOG1(ERR,"ipssh_open_tcp_conn :: ipcom_socket() failed, errno=%d", ipcom_errno);
goto cleanup;
}
if(ipcom_connect(*sock, (struct Ip_sockaddr*)&addr, IPCOM_SA_LEN_GET(&addr)) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR,"ipssh_open_tcp_conn :: ipcom_connect() failed, errno=%d", ipcom_errno);
goto cleanup;
}
rc = IPCOM_SUCCESS;
cleanup:
ipcom_freeaddrinfo(res);
if(*sock != IP_INVALID_SOCKET && rc != IPCOM_SUCCESS)
{
ipcom_socketclose(*sock);
*sock = IP_INVALID_SOCKET;
}
return rc;
}
/*
*===========================================================================
* ipssh_port_fwd_handler_c2s
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_channel2sock_handler(Ipssh_channel* channel, Ipcom_buffer* msg)
{
Ip_u32 len;
Ip_ssize_t rc;
if(ipssh_buffer_get_uint(msg, &len) || ipcom_buffer_len(msg) < len)
{
IPCOM_LOG0(ERR,"port_fwd_handler_c2s :: failed to unpack msg");
if(ipssh_send_channel_close_msg(channel) != IPCOM_SUCCESS)
return IPCOM_ERR_FAILED;
}
IPCOM_LOG2(DEBUG2, "port_fwd_handler_c2s :: ch id: %d, len: %d", channel->peer_ch_id, len);
rc = ipcom_socketwrite(channel->data.port_fwd.sock, ipcom_buffer_ptr2pos(msg, 0), len);
if(rc != (Ip_ssize_t)len)
{
IPCOM_LOG1(ERR,"port_fwd_handler_c2s :: ipcom_socket failed, errno=%d", ipcom_errno);
if(ipssh_send_channel_close_msg(channel) != IPCOM_SUCCESS)
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_consume(msg, len);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_setup_listen_sock
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_setup_listen_sock(const char* addr_str, Ip_u32 port, Ip_fd* sockp)
{
union Ip_sockaddr_union addr;
struct Ip_addrinfo *res;
Ip_err rc = IPCOM_SUCCESS;
Ip_s32 on=1;
*sockp = IP_INVALID_SOCKET;
ipcom_memset(&addr, 0, sizeof(addr));
if(ipcom_getaddrinfo(addr_str, IP_NULL, IP_NULL, &res) != 0)
{
IPCOM_LOG1(ERR,"ipssh_setup_listen_sock :: invalid addr: %s", addr_str);
return IPCOM_ERR_FAILED;
}
ipcom_memcpy(&addr.sa, res->ai_addr, res->ai_addrlen);
addr.sin.sin_port = (Ip_in_port_t) ip_htons(port); /* Port field is at the same offset for both INET and INET6 */
*sockp = ipcom_socket(res->ai_family, IP_SOCK_STREAM, 0);
if(*sockp == IP_INVALID_SOCKET)
{
IPCOM_LOG1(ERR,"ipssh_setup_listen_sock :: ipcom_socket() failed, errno=%d", ipcom_errno);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipcom_setsockopt(*sockp, IP_SOL_SOCKET, IP_SO_REUSEADDR, (void*) &on, sizeof(on)))
{
IPCOM_LOG1(INFO, "ipssh_setup_listen_sock :: ipcom_setsockopt(IP_SO_REUSEADDR) failed, error=%d",
ipcom_errno);
}
if(ipcom_bind(*sockp, (struct Ip_sockaddr*)&addr, IPCOM_SA_LEN_GET(&addr.sa)) != IPCOM_SUCCESS)
{
IPCOM_LOG1(ERR,"ipssh_setup_listen_sock :: ipcom_bind() failed, errno=%d", ipcom_errno);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipcom_listen(*sockp, 5) != IPCOM_SUCCESS)
{
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
cleanup:
ipcom_freeaddrinfo(res);
if(rc != IPCOM_SUCCESS && *sockp != IP_INVALID_SOCKET)
ipcom_socketclose(*sockp);
return rc;
}
/*
*===========================================================================
* ipssh_msg_global_request
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_global_request(Ipssh_conn* ssh_conn)
{
char* cmd = IP_NULL;
Ip_u8 want_reply;
char* addr_str = IP_NULL;
Ip_u32 port;
Ip_s32 rc = IPCOM_SUCCESS;
Ip_fd sock = IP_INVALID_SOCKET;
/* Unpack generic part of the request */
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) ||
!(cmd = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &want_reply))
{
IPCOM_LOG0(ERR, "ipssh_msg_global_request :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipcom_strcmp(cmd, IPSSH_TCPIP_FWD) == 0)
{
/* Unpack the rest of the message */
if(!(addr_str = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &port))
{
IPCOM_LOG0(ERR, "ipssh_msg_global_request :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG4(DEBUG, "ipssh_msg_global_request :: cmd=%s, want reply=%d, addr=%s, port=%d", cmd, want_reply, addr_str, port);
if(!ssh_conn->config.port_fwd)
{
IPCOM_LOG0(INFO,"ipssh_msg_global_request :: " IPSSH_TCPIP_FWD ", port forwarding not turned on");
if(want_reply && ipssh_send_request_failure_msg(ssh_conn) != IPCOM_SUCCESS)
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipssh_setup_listen_sock(addr_str, port, &sock) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR,"ipssh_msg_global_request :: ipssh_setup_listen_sock() failed");
if(want_reply && ipssh_send_request_failure_msg(ssh_conn) != IPCOM_SUCCESS)
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(want_reply && ipssh_send_request_success_msg(ssh_conn) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_global_request :: ipssh_send_request_success_msg() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ssh_conn->port_fwd_cb)
{
if(ssh_conn->port_fwd_cb(ssh_conn, sock, IPSSH_PORT_FWD_EVENT_LISTEN) != IPCOM_SUCCESS)
rc = IPCOM_ERR_FAILED;
else
/* sock is stored in Ipssh_map_entry. */
sock = IP_INVALID_SOCKET;
}
}
else
{
/* Unknown/unsupported command */
IPCOM_LOG2(DEBUG, "ipssh_msg_global_request :: unknown cmd=%s, want reply=%d", cmd, want_reply);
if(want_reply)
rc = ipssh_send_request_failure_msg(ssh_conn);
goto cleanup;
}
cleanup:
if(cmd)
ipcom_free(cmd);
if(addr_str)
ipcom_free(addr_str);
if (sock != IP_INVALID_SOCKET)
ipcom_socketclose(sock);
return rc;
}
/*
*===========================================================================
* ipssh_send_request_success_msg
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_request_success_msg(Ipssh_conn* ssh_conn)
{
Ipcom_buffer* pkt;
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_REQUEST_SUCCESS);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_request_success_msg :: out of memory");
return IPCOM_ERR_FAILED;
}
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
{
IPCOM_LOG0(ERR, "ipssh_send_request_success_msg :: ipssh_send_pkt() failed");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_send_request_failure_msg
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_request_failure_msg(Ipssh_conn* ssh_conn)
{
Ipcom_buffer* pkt;
pkt = ipssh_buffer_new_v2_pkt(32, IPSSH_MSG_REQUEST_FAILURE);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_request_failure_msg :: out of memory");
return IPCOM_ERR_FAILED;
}
if(ipssh_send_pkt(ssh_conn, pkt, IP_TRUE))
{
IPCOM_LOG0(ERR, "ipssh_send_request_success_msg :: ipssh_send_pkt() failed");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_msg_channel_request
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_channel_request(Ipssh_conn* ssh_conn)
{
char* term=IP_NULL;
Ip_u8* term_modes=IP_NULL;
Ip_u32 term_mode_len;
Ip_u32 term_cols;
Ip_u32 term_rows;
Ip_u32 term_w_pix;
Ip_u32 term_h_pix;
char* cmd = IP_NULL;
Ip_u8 want_reply;
Ip_u32 my_ch;
Ip_s32 rc = IPCOM_SUCCESS;
char* subsystem = IP_NULL;
Ipssh_channel* channel;
char* exec_cmd = IP_NULL;
if(ipcom_buffer_consume(ssh_conn->recv_plain_buf, 6) ||
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &my_ch) ||
IP_NULL == (cmd = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &want_reply))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG3(DEBUG, "ipssh_msg_channel_request :: ch=%d, type=%s, want reply=%d",
my_ch, cmd, want_reply);
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, my_ch);
if(!channel)
{
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: no channel data for id=%d", my_ch);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipcom_strcmp(cmd, IPSSH_PTY_REQ) == 0)
{
if(IP_NULL == (term = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) ||
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &channel->data.shell.init_term_cols) ||
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &channel->data.shell.init_term_rows) ||
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_w_pix) ||
ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_h_pix))
{
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: failed to unpack pty data, ch=%d",my_ch);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
ipssh_set_default_term_modes(&channel->data.shell.term_modes);
term_modes = ipssh_buffer_get_sshbuf(ssh_conn->recv_plain_buf, &term_mode_len);
if(!ssh_conn->config.term)
{
IPCOM_LOG0(INFO, "ipssh_msg_channel_request :: Pseudo terminal requested, but terminal connections not active)");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(term_modes && ipssh_parse_terminal_modes(&channel->data.shell.term_modes, term_modes, term_mode_len) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_parse_terminal_modes failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
channel->type = IPSSH_CH_TYPE_SHELL;
if(want_reply)
{
if(ipssh_send_channel_success(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
}
else if(ipcom_strcmp(cmd, IPSSH_SHELL) == 0)
{
if(ipssh_start_shell(ssh_conn, channel) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_start_shell() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
channel->type = IPSSH_CH_TYPE_SHELL;
if(want_reply)
{
if(ipssh_send_channel_success(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 2");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_TERM;
if (channel->data.shell.init_term_cols != 0 && channel->data.shell.init_term_rows != 0
&& ipssh_terminal_resize_cb != IP_NULL)
{
if (ipssh_terminal_resize_cb(channel->data.shell.shell_fd,
channel->data.shell.init_term_cols,
channel->data.shell.init_term_rows) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: terminal_resize_cb failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
}
else if(ipcom_strcmp(cmd, IPSSH_WIN_CHANGE) == 0)
{
if(ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_cols)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_rows)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_w_pix)
|| ipssh_buffer_get_uint(ssh_conn->recv_plain_buf, &term_h_pix))
{
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: failed to unpack window change msg, ch=%d",my_ch);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
/* Ignore if no terminal resize callback is set, or if columns or width is zero */
if (term_cols != 0 && term_rows != 0 && ipssh_terminal_resize_cb != IP_NULL)
if (ipssh_terminal_resize_cb(channel->data.shell.shell_fd, term_cols, term_rows) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: win change: terminal_resize_cb failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
else if(ipcom_strcmp(cmd, IPSSH_SUBSYSTEM_STR) == 0)
{
Ip_fd sock = IP_INVALID_SOCKET;
if(!(subsystem = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_buffer_get_sshstr failed for subsystem");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if (ipssh_channel_init_subsystem(subsystem, channel, &sock) != IPCOM_SUCCESS)
{
IPCOM_LOG1(INFO, "ipssh_msg_channel_request :: ipssh_channel_init_subsystem failed for subsystem %s", subsystem);
if(want_reply)
{
if(ipssh_send_channel_failure(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
}
if (sock != IP_INVALID_SOCKET)
{
int on = 1;
if (ipcom_socketioctl(sock, IP_FIONBIO, (char *)&on))
{
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: ipcom_socketioctl() failed on subsystem socket, errno=%d", ipcom_errno);
goto cleanup;
}
if(!ipssh_store_map_entry(ssh_conn->ssh_conn_ctx, sock, ipssh_port_fwd_handler_s2c, 0))
{
IPCOM_LOG1(ERR, "ipssh_msg_channel_request :: ipssh_store_map_entry() failed", ipcom_errno);
goto cleanup;
}
channel->data.port_fwd.sock = sock;
channel->handler = ipssh_channel2sock_handler;
IP_FD_SET(sock, &(channel->ssh_conn->ssh_conn_ctx->read_set));
}
if(want_reply)
{
if(ipssh_send_channel_success(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 3");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
}
else if(ipcom_strcmp(cmd, IPSSH_X11_REQ) == 0)
{
Ip_u8 single_connection;
char* x11_auth_proto = IP_NULL;
char* x11_auth_cookie = IP_NULL;
int x11_screen_no;
/* X11 forwarding not supported/needed, Unpack the message and ignore.*/
if(ipcom_buffer_get_char(ssh_conn->recv_plain_buf, &single_connection)
|| ((x11_auth_proto = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) == IP_NULL)
|| ((x11_auth_cookie = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf)) == IP_NULL)
|| ipcom_buffer_get_int(ssh_conn->recv_plain_buf, (Ip_s32*)&x11_screen_no))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: failed to unpack X11 req");
rc = IPCOM_ERR_FAILED;
if (x11_auth_proto)
ipcom_free(x11_auth_proto);
if (x11_auth_cookie)
ipcom_free(x11_auth_cookie);
goto cleanup;
}
ipcom_free(x11_auth_proto);
ipcom_free(x11_auth_cookie);
/*Send channel failure if reply is requested */
if(want_reply)
{
if(ipssh_send_channel_failure(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
}
else if(ipcom_strcmp(cmd, IPSSH_EXEC) == 0)
{
exec_cmd = ipssh_buffer_get_sshstr(ssh_conn->recv_plain_buf);
if (!exec_cmd)
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: failed to unpack exec command");
if(want_reply)
{
if(ipssh_send_channel_failure(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(want_reply)
{
if(ipssh_send_channel_success(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 4");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
if (ipssh_handle_exec_cmd(ssh_conn, channel, exec_cmd) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_handle_exec_cmd failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
else if(ipcom_strcmp(cmd, IPSSH_EXIT_STATUS) == 0)
{
if(want_reply)
{
if(ipssh_send_channel_success(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_success() failed 4");
}
}
rc = IPCOM_ERR_PERMISSION_DENIED;
goto cleanup;
}
else
{
/* Unsupported request received. Send channel failure if reply is requested. */
IPCOM_LOG2(DEBUG, "ipssh_msg_channel_request :: unknown type: %s, want reply=%d. Ignoring", cmd, want_reply);
if(want_reply)
{
if(ipssh_send_channel_failure(channel))
{
IPCOM_LOG0(ERR, "ipssh_msg_channel_request :: ipssh_send_channel_failure() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
}
cleanup:
if(cmd)
ipcom_free(cmd);
if(term)
ipcom_free(term);
if(term_modes)
ipcom_free(term_modes);
if(subsystem)
ipcom_free(subsystem);
if(exec_cmd)
ipcom_free(exec_cmd);
return rc;
}
/*
*===========================================================================
* ipssh_shell_exit
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_shell_exit2(Ipssh_conn* ssh_conn, Ip_fd shell_fd)
{
Ipssh_channel* channel;
Ip_err rc;
channel = ipssh_channel_map_lookup(&ssh_conn->ch_map, shell_fd);
if(!channel)
{
IPCOM_LOG1(ERR, "ipssh_shell_exit :: failed to lookup channel for shell_fd %d", shell_fd);
return IPCOM_ERR_FAILED;
}
#ifdef IPSSH_USE_V1
if(ssh_conn->ssh_version == 1)
{
rc = ipssh_send_msg_v1_exit_status(ssh_conn, 0);
}
else
#endif
{
(void)ipssh_send_channel_exit(channel);
rc = ipssh_send_channel_close_msg(channel);
}
/* If this is the last channel, make sure the connection will be shutdown. */
if(ssh_conn->ch_map.size == 1)
{
/* The ssh server is hanging in a select call. Close the socket to release the daemon, who will do the cleanup */
ipcom_socketclose(ssh_conn->sock);
ssh_conn->sock = IP_INVALID_SOCKET;
}
return rc;
}
/*
*===========================================================================
* ipssh_set_terminal_resize_cb
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC void
ipssh_set_terminal_resize_cb(Ipssh_terminal_resize_cb terminal_resize_cb)
{
ipssh_terminal_resize_cb = terminal_resize_cb;
}
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/