1092 lines
30 KiB
C
1092 lines
30 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_connection_v1.c,v $ $Revision: 1.64.10.3 $
|
|
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_connection_v1.c,v $
|
|
* $Author: jren $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Roger Boden <roger@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include <ipssh_config.h>
|
|
|
|
#ifdef IPSSH_USE_V1
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipssh_h.h"
|
|
#include "ipssh_buffer.h"
|
|
#include "ipssh_channel.h"
|
|
#include "ipssh_transport_v1.h"
|
|
#include "ipssh_connection_v1.h"
|
|
#include "ipssh_connection.h"
|
|
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_err.h>
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPSSH_PORT_FWD_CHUNK 2048
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_lookup_port_fwd_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_lookup_port_fwd_addr(Ipcom_list* map, Ip_fd sock, char** addr, Ip_u32* port)
|
|
{
|
|
Ipcom_list* current;
|
|
for(current = ipcom_list_first(map); current; current = ipcom_list_next(current))
|
|
{
|
|
Ipssh_port_fwd_addr_map* entry = (Ipssh_port_fwd_addr_map*) current;
|
|
if(entry->sock == sock)
|
|
{
|
|
*addr = entry->addr;
|
|
*port = entry->port;
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_store_port_fwd_addr
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_store_port_fwd_addr(Ipcom_list* map, Ip_fd sock, const char* addr, const Ip_u32 port)
|
|
{
|
|
Ipssh_port_fwd_addr_map* entry;
|
|
|
|
entry = ipcom_malloc(sizeof(Ipssh_port_fwd_addr_map));
|
|
if(!entry)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
entry->addr = ipcom_malloc(ipcom_strlen(addr)+1);
|
|
if(!entry->addr)
|
|
{
|
|
ipcom_free(entry);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
ipcom_strcpy(entry->addr, addr);
|
|
entry->port = port;
|
|
entry->sock = sock;
|
|
ipcom_list_insert_last(map, (Ipcom_list*) entry);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_port_fwd_listen_handler_v1
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_port_fwd_listen_handler_v1(Ip_fd sock, Ipssh_conn_ctx* ctx, Ipssh_map_entry* map_entry)
|
|
{
|
|
Ip_fd clt_sock;
|
|
Ipssh_channel* channel;
|
|
Ipcom_buffer* pkt;
|
|
char* conn_addr;
|
|
Ip_u32 conn_port;
|
|
|
|
(void) map_entry;
|
|
|
|
clt_sock = ipcom_accept(sock, IP_NULL, IP_NULL);
|
|
if(clt_sock == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_port_fwd_listen_handler_v1 :: ipcom_accept() failed, errno=%d", ipcom_errno);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
channel = ipssh_channel_new(ctx->ssh_conn, 0xffffffff, ipssh_channel2sock_handler);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler_v1 :: ipssh_channel_new failed");
|
|
ipcom_socketclose(clt_sock);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
channel->data.port_fwd.sock = clt_sock;
|
|
channel->type = IPSSH_CH_TYPE_PORT_FWD;
|
|
|
|
if(ipssh_lookup_port_fwd_addr(&(ctx->ssh_conn->port_fwd_addr_map), sock, &conn_addr, &conn_port) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler_v1 :: no connect addr for port forward socket");
|
|
ipcom_socketclose(clt_sock);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
pkt = ipssh_buffer_new_v1_pkt(256, IPSSH_MSG_V1_PORT_OPEN);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler_v1 :: out of memory");
|
|
ipcom_socketclose(clt_sock);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
/* Open channel to convey forwarded tcp connection */
|
|
if( ipcom_buffer_put_int(pkt, ip_htonl(channel->my_ch_id)) ||
|
|
ipssh_buffer_put_sshstr(pkt, conn_addr) ||
|
|
ipcom_buffer_put_int(pkt, ip_htonl(conn_port)) )
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler_v1 :: failed to pack message");
|
|
ipcom_buffer_free(pkt);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if(ipssh_send_v1_pkt(ctx->ssh_conn, pkt) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_port_fwd_listen_handler_v1 :: failed to send message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_parse_terminal_modes_v1
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_parse_terminal_modes_v1(Ipcom_buffer* pkt, Ipssh_term_modes* term_modes)
|
|
{
|
|
Ip_u8 mode;
|
|
Ip_u8 small_value;
|
|
Ip_u32 big_value;
|
|
|
|
while(ipcom_buffer_len(pkt) > 0)
|
|
{
|
|
(void)ipcom_buffer_get_char(pkt, &mode);
|
|
if(mode == IPSSH_TTY_OP_END)
|
|
break;
|
|
if(mode < 128)
|
|
{
|
|
if(ipcom_buffer_get_char(pkt, &small_value))
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
else
|
|
{
|
|
if(ipssh_buffer_get_uint(pkt, &big_value))
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
switch(mode)
|
|
{
|
|
case IPSSH_ONLCR:
|
|
term_modes->ONLCR = small_value; /*lint !e644*/
|
|
break;
|
|
default:
|
|
/* NOP */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_setup_stdio_channel
|
|
*===========================================================================
|
|
* Description:
|
|
* In SSH v1 no channel is used to convey stdio data. Instead dedicated messages
|
|
* are used. However, we create a channel struct to hold terminal session
|
|
* data anyway
|
|
*
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipssh_v1_setup_stdio_channel(Ipssh_conn* ssh_conn)
|
|
{
|
|
ssh_conn->stdio_channel = ipssh_channel_new(ssh_conn, 0xffffffff, IP_NULL);
|
|
if(!ssh_conn->stdio_channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_setup_stdio_channel :: ipssh_channel_new() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
ssh_conn->stdio_channel->type = IPSSH_CH_TYPE_SHELL;
|
|
|
|
ipssh_set_default_term_modes(&ssh_conn->stdio_channel->data.shell.term_modes);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_exec_cmd
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_exec_cmd(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ip_err rc;
|
|
char* cmd;
|
|
|
|
if (!ssh_conn->stdio_channel && ipssh_v1_setup_stdio_channel(ssh_conn) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_exec_cmd :: ipssh_v1_setup_stdio_channel() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_TERM;
|
|
|
|
cmd = ipssh_buffer_get_sshstr(pkt);
|
|
if (!cmd)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_exec_cmd :: failed to unpack cmd");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
rc = ipssh_handle_exec_cmd(ssh_conn, ssh_conn->stdio_channel, cmd);
|
|
|
|
ipcom_free(cmd);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_exec_shell
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_exec_shell(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
(void) pkt;
|
|
|
|
if (!ssh_conn->stdio_channel && ipssh_v1_setup_stdio_channel(ssh_conn) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_exec_shell :: ipssh_v1_setup_stdio_channel() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
ssh_conn->stdio_channel->data.shell.shell_fd =
|
|
ipssh_sock_shell_start(ssh_conn, ssh_conn->user, ssh_conn->sh_ctx);
|
|
|
|
if(ssh_conn->stdio_channel->data.shell.shell_fd == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_exec_shell :: shell_start_cb failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_TERM;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_stdin_data
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_stdin_data(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ip_u8* data;
|
|
Ip_u32 in_len;
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
|
|
if( !(data=ipssh_buffer_get_sshbuf(pkt, &in_len)) )
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_stdin_data :: failed to unpack message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
ipssh_handle_term_modes(data, in_len, &(ssh_conn->stdio_channel->data.shell.term_modes));
|
|
|
|
if(ipssh_sock_shell_stdin(ssh_conn->stdio_channel->data.shell.shell_fd, (const char*)data, in_len) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_stdin_data :: shell_stdin_cb failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
#ifdef IPSSH_ECHO_STDIN
|
|
{
|
|
Ipcom_buffer* echo_pkt;
|
|
Ip_u8 last_chr = 0;
|
|
Ip_u32 pos = 0;
|
|
int len_pos;
|
|
Ip_u32 tmp_len;
|
|
|
|
next_line:
|
|
/* Required packet len = length of data to echo + length field + max pad len + packet type + crc + margin */
|
|
echo_pkt = ipssh_buffer_new_v1_pkt(in_len+4+8+1+4+8, IPSSH_SMSG_V1_STDOUT_DATA);
|
|
if(!echo_pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_stdin_data :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
len_pos = ipcom_buffer_len(echo_pkt);
|
|
(void)ipcom_buffer_decr_cap(echo_pkt, 4);
|
|
tmp_len = ipcom_buffer_len(echo_pkt);
|
|
|
|
while(pos < in_len)
|
|
{
|
|
ipssh_store_echo_char(echo_pkt, &(ssh_conn->stdio_channel->data.shell.term_modes), data[pos], &last_chr);
|
|
/* Send a line at a time or all data, whatever occurs first */
|
|
pos++;
|
|
if(pos == in_len || data[pos] == '\r' || data[pos] == '\n'
|
|
|| ipcom_buffer_capacity(echo_pkt)<=(4+2) /* Leave room for crc */)
|
|
{
|
|
IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(echo_pkt, len_pos), ipcom_buffer_len(echo_pkt)-tmp_len);
|
|
if(ipssh_send_v1_pkt(ctx, echo_pkt) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_stdin_data :: failed to echo data");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
if(pos < in_len)
|
|
goto next_line;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
cleanup:
|
|
if(data)
|
|
ipcom_free(data);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_eof
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_eof(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
(void) ssh_conn;
|
|
(void) pkt;
|
|
|
|
if (ipcom_shutdown(ssh_conn->stdio_channel->data.shell.shell_fd, IP_SHUT_WR))
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_window_size
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_window_size(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ip_u32 rows;
|
|
Ip_u32 columns;
|
|
Ip_u32 width;
|
|
Ip_u32 heigth;
|
|
|
|
(void) ssh_conn;
|
|
|
|
if(ipssh_buffer_get_uint(pkt, &rows) ||
|
|
ipssh_buffer_get_uint(pkt, &columns) ||
|
|
ipssh_buffer_get_uint(pkt, &width) ||
|
|
ipssh_buffer_get_uint(pkt, &heigth))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_window_size :: failed to unpack message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_msg_v1_exit_status
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_msg_v1_exit_status(Ipssh_conn* ssh_conn, Ip_u32 status)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
Ip_err rc;
|
|
|
|
pkt = ipssh_buffer_new_v1_pkt(64, IPSSH_SMSG_V1_EXITSTATUS);
|
|
if(!pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_send_msg_v1_exit_status :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(status));
|
|
|
|
rc = ipssh_send_v1_pkt(ssh_conn, pkt);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_request_pty
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_request_pty(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
char* term;
|
|
Ip_u32 rows;
|
|
Ip_u32 columns;
|
|
Ip_u32 pixel_height;
|
|
Ip_u32 pixel_width;
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
|
|
if( !(term = ipssh_buffer_get_sshstr(pkt)) ||
|
|
ipssh_buffer_get_uint(pkt, &rows) ||
|
|
ipssh_buffer_get_uint(pkt, &columns) ||
|
|
ipssh_buffer_get_uint(pkt, &pixel_width) ||
|
|
ipssh_buffer_get_uint(pkt, &pixel_height) )
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_request_pty :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ipssh_v1_setup_stdio_channel(ssh_conn) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_request_pty :: ipssh_v1_setup_stdio_channel() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(ipssh_parse_terminal_modes_v1(pkt, &ssh_conn->stdio_channel->data.shell.term_modes) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_request_pty :: ipssh_parse_terminal_modes_v1 failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipssh_send_msg_v1_no_body(ssh_conn, IPSSH_SMSG_V1_SUCCESS) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_request_pty :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if(term)
|
|
ipcom_free(term);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_port_open
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_port_open(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
Ip_u32 ch_id;
|
|
char* host_str = IP_NULL;
|
|
Ip_u32 port;
|
|
Ipssh_channel* channel;
|
|
Ip_fd sock;
|
|
|
|
/* Unpack message */
|
|
if( ipssh_buffer_get_uint(pkt, &ch_id) ||
|
|
!(host_str = ipssh_buffer_get_sshstr(pkt)) ||
|
|
ipssh_buffer_get_uint(pkt, &port) )
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_port_open :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(!ssh_conn->config.port_fwd)
|
|
{
|
|
IPCOM_LOG0(INFO, "ipssh_v1_msg_port_open :: port forwarding not turned on");
|
|
if(ipssh_send_msg_v1_channel_open_failure(ssh_conn, ch_id))
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
if(ipssh_open_tcp_conn(host_str, port, &sock) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_port_open :: ipssh_open_tcp_conn() failed");
|
|
if(ipssh_send_msg_v1_channel_open_failure(ssh_conn, ch_id))
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
channel = ipssh_channel_new(ssh_conn, ch_id, ipssh_channel2sock_handler);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_port_open :: ipssh_channel_new2");
|
|
ipcom_socketclose(sock);
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
channel->data.port_fwd.sock = sock;
|
|
channel->type = IPSSH_CH_TYPE_PORT_FWD;
|
|
|
|
if(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)
|
|
{
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if(ipssh_send_msg_v1_channel_open_confirmation(ssh_conn, channel) != IPCOM_SUCCESS)
|
|
rc = IPCOM_ERR_FAILED;
|
|
|
|
cleanup:
|
|
if(host_str)
|
|
ipcom_free(host_str);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_port_forward_request
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_port_forward_request(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ip_u32 srv_port;
|
|
Ip_u32 conn_port;
|
|
char* host = IP_NULL;
|
|
Ip_err rc = IPCOM_SUCCESS;
|
|
Ip_fd sock;
|
|
|
|
/* Unpack message */
|
|
if(ipssh_buffer_get_uint(pkt, &srv_port) ||
|
|
!(host=ipssh_buffer_get_sshstr(pkt)) ||
|
|
ipssh_buffer_get_uint(pkt, &conn_port))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_port_forward_request :: failed to unpack message");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(!ssh_conn->config.port_fwd)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_port_forward_request :: port forwarding not turned on");
|
|
if(ipssh_send_msg_v1_no_body(ssh_conn, IPSSH_SMSG_V1_FAILURE))
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipssh_setup_listen_sock("0.0.0.0", srv_port, &sock) != IPCOM_SUCCESS)
|
|
{
|
|
if(ipssh_send_msg_v1_no_body(ssh_conn, IPSSH_SMSG_V1_FAILURE))
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(ipssh_store_port_fwd_addr(&(ssh_conn->port_fwd_addr_map), sock, host, conn_port) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_port_forward_request :: ipssh_store_port_fwd_addr() 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;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if(ipssh_send_msg_v1_no_body(ssh_conn, IPSSH_SMSG_V1_SUCCESS))
|
|
rc = IPCOM_ERR_FAILED;
|
|
|
|
cleanup:
|
|
if(host)
|
|
ipcom_free(host);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_channel_close
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_channel_close(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ip_u32 my_ch_id;
|
|
Ipcom_buffer* send_pkt;
|
|
|
|
if(ipssh_buffer_get_uint(pkt, &my_ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_channel_close :: failed to unpack message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, my_ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(DEBUG2, "ipssh_v1_msg_channel_close :: no channel data for id=%d", my_ch_id);
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
send_pkt = ipssh_buffer_new_v1_pkt(64, IPSSH_MSG_V1_CHANNEL_CLOSE_CONFIRMATION);
|
|
if(!send_pkt)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_channel_close :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
(void)ipcom_buffer_put_int(send_pkt, ip_htonl(channel->peer_ch_id));
|
|
if(ipssh_send_v1_pkt(ssh_conn, send_pkt))
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
ipssh_channel_close(channel);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_channel_data
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_channel_data(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ip_u32 my_ch_id;
|
|
|
|
if(ipssh_buffer_get_uint(pkt, &my_ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_channel_data :: 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_v1_msg_channel_data :: no channel data for id=%d", my_ch_id);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
if(channel->handler(channel, pkt) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_channel_data :: channel handler failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_channel_close_confirmation
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_channel_close_confirmation(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ip_u32 my_ch_id;
|
|
Ipssh_channel* channel;
|
|
|
|
if(ipssh_buffer_get_uint(pkt, &my_ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_channel_close_confirmation :: failed to unpack message");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
channel = ipssh_channel_id2ch(&ssh_conn->ch_map, my_ch_id);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_msg_channel_close_confirmation :: failed to unpack message");
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
ipssh_channel_close(channel);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_msg_v1_channel_data
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_msg_v1_channel_data(Ipssh_conn* ssh_conn, Ip_u32 ch_id, Ip_u8* data, Ip_size_t len)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
/* pkt len = pkt len+max pad len+pkt type+ch id+data len+data+margin(10)*/
|
|
pkt = ipssh_buffer_new_v1_pkt(4+8+1+4+4+len +10, IPSSH_MSG_V1_CHANNEL_DATA);
|
|
if(!pkt)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(ch_id));
|
|
(void)ipssh_buffer_put_sshbuf(pkt, data, len);
|
|
|
|
if(ipssh_send_v1_pkt(ssh_conn, pkt) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_msg_v1_channel_open_confirmation
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_msg_v1_channel_open_confirmation(Ipssh_conn* ssh_conn, Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
pkt = ipssh_buffer_new_v1_pkt(64, IPSSH_MSG_V1_CHANNEL_OPEN_CONFIRMATION);
|
|
if(!pkt)
|
|
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));
|
|
|
|
if(ipssh_send_v1_pkt(ssh_conn, pkt) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_msg_v1_channel_open_failure
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_msg_v1_channel_open_failure(Ipssh_conn* ssh_conn, Ip_u32 ch_id)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
pkt = ipssh_buffer_new_v1_pkt(64, IPSSH_MSG_V1_CHANNEL_OPEN_FAILURE);
|
|
if(!pkt)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(ch_id));
|
|
|
|
if(ipssh_send_v1_pkt(ssh_conn, pkt) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_msg_v1_channel_close
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_send_msg_v1_channel_close(Ipssh_conn* ssh_conn, Ipssh_channel* channel)
|
|
{
|
|
Ipcom_buffer* pkt;
|
|
|
|
pkt = ipssh_buffer_new_v1_pkt(32, IPSSH_MSG_V1_CHANNEL_CLOSE);
|
|
if(!pkt)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
(void)ipcom_buffer_put_int(pkt, ip_htonl(channel->peer_ch_id));
|
|
|
|
if(ipssh_send_v1_pkt(ssh_conn, pkt) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_channel_open_confirmation
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_channel_open_confirmation(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ip_u32 my_ch_id;
|
|
Ip_u32 peer_ch_id;
|
|
|
|
/* Unpack message */
|
|
if(ipssh_buffer_get_uint(pkt, &my_ch_id) ||
|
|
ipssh_buffer_get_uint(pkt, &peer_ch_id))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_v1_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_v1_msg_channel_open_confirmation :: no data for ch id=%d", my_ch_id);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
channel->peer_ch_id = peer_ch_id;
|
|
if(ssh_conn->port_fwd_cb)
|
|
if(ssh_conn->port_fwd_cb(ssh_conn, channel->data.port_fwd.sock, IPSSH_PORT_FWD_EVENT_OPEN))
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_v1_msg_exit_confirmation
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_v1_msg_exit_confirmation(Ipssh_conn* ssh_conn, Ipcom_buffer* pkt)
|
|
{
|
|
(void) ssh_conn;
|
|
(void) pkt;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_port_fwd_handler_s2c_v1
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipssh_port_fwd_handler_s2c_v1(Ip_fd sock, Ipssh_conn_ctx* ctx, Ipssh_map_entry* map_entry)
|
|
{
|
|
Ipssh_channel* channel;
|
|
Ip_u8* data;
|
|
Ip_s32 rc = IPCOM_SUCCESS;
|
|
Ip_ssize_t read_bytes;
|
|
|
|
channel = ipssh_sock2channel(&ctx->ssh_conn->ch_map, sock);
|
|
if(!channel)
|
|
{
|
|
IPCOM_LOG1(ERR, "port_fwd_handler_s2c_v1 :: no channel data for socket: %d", sock);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
data = ipcom_malloc(IPSSH_PORT_FWD_CHUNK);
|
|
if(!data)
|
|
{
|
|
IPCOM_LOG0(ERR, "port_fwd_handler_s2c_v1 :: out of memory");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
read_bytes = ipcom_socketread(sock, data, IPSSH_PORT_FWD_CHUNK);
|
|
if(read_bytes <= 0) /* Port fwd connection closed */
|
|
{
|
|
if(read_bytes < 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "port_fwd_handler_s2c_v1 :: connection lost, errno: %d", ipcom_errno);
|
|
}
|
|
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_msg_v1_channel_close(ctx->ssh_conn, 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 */
|
|
{
|
|
if(ipssh_send_msg_v1_channel_data(ctx->ssh_conn, channel->peer_ch_id, data, read_bytes) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "port_fwd_handler_s2c_v1 :: ipssh_send_channel_data_msg() failed");
|
|
rc = IPCOM_ERR_FAILED;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
if(data)
|
|
ipcom_free(data);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#else
|
|
void ipssh_connection_v1_empty_file(void);
|
|
void ipssh_connection_v1_empty_file(void) { }
|
|
#endif /* IPSSH_USE_V1 */
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|