462 lines
13 KiB
C
462 lines
13 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_c_api.c,v $ $Revision: 1.4.8.2 $
|
|
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_c_api.c,v $
|
|
* $Author: svc-cmnet $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Roger Boden <roger.boden@windriver.com>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipssh_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include "ipcom_clib.h"
|
|
#include <ipcom_type.h>
|
|
#include "ipcom_cstyle.h"
|
|
#include "ipcom_err.h"
|
|
#include "ipcom_os.h"
|
|
#include "ipcom_sock.h"
|
|
#include "ipcom_sock2.h"
|
|
#include "ipcom_strlib.h"
|
|
#ifdef IPCOM_USE_SYSLOG
|
|
#undef IPCOM_USE_SYSLOG
|
|
#endif
|
|
#include "ipcom_syslog.h"
|
|
|
|
#include "ipssh.h"
|
|
/* #include "ipssh_h.h" */
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
struct Ipssh_c_api_params_st
|
|
{
|
|
Ip_u16 port;
|
|
Ip_u16 pad;
|
|
int sh_param_len;
|
|
Ip_u8 sh_param[1];
|
|
};
|
|
typedef struct Ipssh_c_api_params_st Ipssh_c_api_params;
|
|
|
|
|
|
struct Ipssh_c_api_ctx_st
|
|
{
|
|
Ip_fd stdio_sock;
|
|
Ip_fd peer_sock;
|
|
};
|
|
typedef struct Ipssh_c_api_ctx_st Ipssh_c_api_ctx;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
IPCOM_PROCESS(ipssh_c_api_proxy);
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
IPCOM_PROCESS(ipssh_c_api_proxy)
|
|
{
|
|
Ipssh_c_api_params *sinfo;
|
|
char read_buf[256];
|
|
struct Ip_addrinfo hints;
|
|
struct Ip_linger linger;
|
|
int enable = IP_TRUE;
|
|
char stdio_port_str[6];
|
|
struct Ip_addrinfo *res = IP_NULL;
|
|
int sockerr;
|
|
void* shell_handle;
|
|
Ipcom_shell_ctx* sh_ctx = IP_NULL;
|
|
Ipssh_c_api_ctx* proxy_ctx = IP_NULL;
|
|
char* user = IP_NULL;
|
|
|
|
ipcom_proc_init();
|
|
|
|
if (ipcom_ipc_install() != IPCOM_SUCCESS)
|
|
goto exit;
|
|
|
|
if (ipcom_ipc_receive(IP_NULL, &sinfo, IPC_TIMEOUT_INFINITE) != IPCOM_SUCCESS)
|
|
goto exit;
|
|
(void)ipcom_ipc_uninstall();
|
|
|
|
sh_ctx = ipcom_shell_ctx_new(sinfo->sh_param_len);
|
|
if (!sh_ctx)
|
|
goto exit;
|
|
sh_ctx->len = sinfo->sh_param_len;
|
|
ipcom_memcpy(sh_ctx->buf, sinfo->sh_param, sinfo->sh_param_len);
|
|
|
|
/* Get a tcp socket and connect to the SSH daemon listening port */
|
|
ipcom_memset(&hints, 0, sizeof(hints));
|
|
#ifdef IPCOM_USE_INET
|
|
hints.ai_family = IP_AF_INET;
|
|
#else
|
|
hints.ai_family = IP_AF_INET6;
|
|
#endif
|
|
hints.ai_socktype = IP_SOCK_STREAM;
|
|
ipcom_sprintf(stdio_port_str, "%d", (int)sinfo->port);
|
|
ipcom_ipc_free(sinfo);
|
|
if (ipcom_getaddrinfo(IP_NULL, stdio_port_str, &hints, &res) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_c_api_proxy :: ipcom_getaddrinfo() failed, errno = %d", ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
proxy_ctx = ipcom_malloc(sizeof(Ipssh_c_api_ctx));
|
|
if (!proxy_ctx)
|
|
goto exit;
|
|
proxy_ctx->stdio_sock = ipcom_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (proxy_ctx->stdio_sock == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_c_api_proxy :: ipcom_socket(IP_AF_INET) failed, errno = %d", ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
/* Note! the SSH daemon side will get a "connection reset by peer" when exiting the
|
|
shell since the linger time is set to 0 */
|
|
linger.l_onoff = 1;
|
|
linger.l_linger = 0;
|
|
(void)ipcom_setsockopt(proxy_ctx->stdio_sock, IP_SOL_SOCKET, IP_SO_LINGER, &linger, sizeof(linger));
|
|
|
|
/*
|
|
* Disable Nagle (enable no delay) on this socket since it is an
|
|
* interactive connection
|
|
*/
|
|
(void)ipcom_setsockopt(proxy_ctx->stdio_sock, IP_IPPROTO_TCP, IP_TCP_NODELAY, &enable, sizeof(enable));
|
|
|
|
sockerr = ipcom_connect(proxy_ctx->stdio_sock, res->ai_addr, res->ai_addrlen);
|
|
if (sockerr == IP_SOCKERR)
|
|
{
|
|
IPCOM_LOG2(ERR,
|
|
"ipssh_c_api_proxy :: ipcom_connect(IP_AF_INET%s) failed, errno = %d",
|
|
res->ai_family == IP_AF_INET ? "" : "6",
|
|
ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
if (ipcom_shell_ctx_get_int(sh_ctx, IPSSH_CLT_FD, &proxy_ctx->peer_sock) != IPCOM_SUCCESS)
|
|
goto exit;
|
|
user = ipcom_shell_ctx_get_user(sh_ctx);
|
|
shell_handle = ipssh_shell_start_cb(proxy_ctx, user, sh_ctx);
|
|
if (user)
|
|
ipcom_free(user);
|
|
|
|
if (!shell_handle)
|
|
goto exit;
|
|
|
|
if (ipssh_shell_welcome_msg_cb(proxy_ctx, shell_handle) != IPCOM_SUCCESS)
|
|
goto exit;
|
|
|
|
/* Main stdio proxy loop */
|
|
for (;;)
|
|
{
|
|
Ip_ssize_t read_bytes;
|
|
|
|
read_bytes = ipcom_socketread(proxy_ctx->stdio_sock, read_buf, sizeof(read_buf));
|
|
if (read_bytes <= 0)
|
|
{
|
|
(void)ipssh_shell_stop_cb(proxy_ctx, shell_handle);
|
|
goto exit;
|
|
}
|
|
|
|
if (ipssh_shell_stdin_cb(proxy_ctx, shell_handle, read_buf, read_bytes) != IPCOM_SUCCESS)
|
|
goto exit;
|
|
|
|
}
|
|
|
|
exit:
|
|
if (proxy_ctx)
|
|
{
|
|
if (proxy_ctx->stdio_sock != IP_INVALID_SOCKET)
|
|
ipcom_socketclose(proxy_ctx->stdio_sock);
|
|
ipcom_free(proxy_ctx);
|
|
}
|
|
if (res != IP_NULL)
|
|
ipcom_freeaddrinfo(res);
|
|
if (sh_ctx)
|
|
ipcom_shell_ctx_free(sh_ctx);
|
|
|
|
ipcom_proc_exit();
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipssh_start_shell_c_api(Ip_fd *stdio_sock, const Ipcom_shell_ctx* sh_ctx)
|
|
{
|
|
Ip_fd mother = IP_INVALID_SOCKET;
|
|
Ip_fd shell_fd = IP_INVALID_SOCKET;
|
|
Ip_socklen_t addr_len;
|
|
Ipcom_ipc ipc;
|
|
Ipssh_c_api_params *sinfo;
|
|
char procname[40];
|
|
Ip_err retval;
|
|
Ipcom_proc_attr attr;
|
|
Ip_bool ipc_opened = IP_FALSE;
|
|
Ip_pid_t ppid, shell_pid;
|
|
Ip_u16 mother_port;
|
|
static Ip_u32 seqno = 0;
|
|
struct Ip_addrinfo hints;
|
|
struct Ip_addrinfo *res = IP_NULL;
|
|
union Ip_sockaddr_union sa;
|
|
Ip_size_t sh_len = (Ip_size_t) (sh_ctx ? sh_ctx->len : 0);
|
|
|
|
*stdio_sock = IP_INVALID_SOCKET;
|
|
|
|
sinfo = ipcom_ipc_malloc(sizeof(Ipssh_c_api_params)+sh_len);
|
|
if (sinfo == IP_NULL)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_start_shell_c_api :: ipcom_ipc_malloc() failed");
|
|
goto exit;
|
|
}
|
|
|
|
sinfo->sh_param_len = (int) sh_len;
|
|
if (sh_ctx)
|
|
ipcom_memcpy(sinfo->sh_param, sh_ctx->buf, (Ip_size_t)sh_ctx->len);
|
|
|
|
/* Get a tcp socket and let the stack pick a port */
|
|
ipcom_memset(&hints, 0, sizeof(hints));
|
|
#ifdef IPCOM_USE_INET
|
|
hints.ai_family = IP_AF_INET;
|
|
#else
|
|
hints.ai_family = IP_AF_INET6;
|
|
|
|
#endif
|
|
hints.ai_socktype = IP_SOCK_STREAM;
|
|
if (ipcom_getaddrinfo(IP_NULL, "0", &hints, &res) != 0)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_start_shell_c_api :: ipcom_getaddrinfo() failed, errno = %d", ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
mother = ipcom_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (mother == IP_INVALID_SOCKET)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_start_shell_c_api :: ipcom_socket(IP_AF_INET) failed, errno = %d", ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
if (ipcom_bind(mother, res->ai_addr, (Ip_socklen_t)res->ai_addrlen) == IP_SOCKERR)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_start_shell_c_api :: ipcom_bind(IP_AF_INET) failed, errno = %d", ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
/* Find out which port was assigned. */
|
|
addr_len = sizeof(sa);
|
|
if (ipcom_getsockname(mother, (struct Ip_sockaddr *)&sa, &addr_len) == IP_SOCKERR)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_start_shell_c_api :: ipcom_getsockname(IP_AF_INET) failed, errno = %d", ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (sa.sa.sa_family == IP_AF_INET)
|
|
mother_port = ip_ntohs(sa.sin.sin_port);
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (sa.sa.sa_family == IP_AF_INET6)
|
|
mother_port = ip_ntohs(sa.sin6.sin6_port);
|
|
#endif
|
|
|
|
if (ipcom_listen(mother, 1) == IP_SOCKERR)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_start_shell_c_api :: ipcom_listen() failed, errno = %d", ipcom_errno);
|
|
goto exit;
|
|
}
|
|
|
|
/* Create and start the shell process. */
|
|
ipcom_proc_attr_init(&attr);
|
|
#ifdef IP_PORT_VXWORKS
|
|
attr.stacksize = IPCOM_PROC_STACK_LARGE;
|
|
#else
|
|
attr.priority = IPCOM_PRIORITY_DEFAULT;
|
|
#endif /* IP_PORT_VXWORKS */
|
|
ppid = ipcom_getpid();
|
|
ipcom_sprintf(procname, "c_api_proxy_%lx_%d", (long)(ppid & 0xffffffff), (int)++seqno);
|
|
if (ipcom_proc_acreate(procname, (Ipcom_proc_func)ipssh_c_api_proxy, &attr, &shell_pid))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipssh_start_shell_c_api :: ipcom_proc_acreate() failed");
|
|
goto exit;
|
|
}
|
|
|
|
/* Open IPC with c_api_proxy. */
|
|
retval = ipcom_ipc_open(&ipc, procname, -1);
|
|
if (retval != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG2(ERR, "ipssh_start_shell_c_api :: ipcom_ipc_open(%s) failed, ret = %d", procname, retval);
|
|
goto exit;
|
|
}
|
|
ipc_opened = IP_TRUE;
|
|
|
|
/* Send a message to ipcom_shell. */
|
|
sinfo->port = mother_port;
|
|
retval = ipcom_ipc_send(&ipc, sinfo);
|
|
if (retval != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipssh_start_shell_c_api :: ipcom_ipc_send() failed, ret = %d", retval);
|
|
goto exit;
|
|
}
|
|
|
|
/* Wait for the shell process to connect */
|
|
shell_fd = ipcom_accept(mother, IP_NULL, IP_NULL);
|
|
|
|
exit:
|
|
if (mother != IP_INVALID_SOCKET)
|
|
(void)ipcom_socketclose(mother);
|
|
if (res != IP_NULL)
|
|
ipcom_freeaddrinfo(res);
|
|
if (ipc_opened)
|
|
(void)ipcom_ipc_close(&ipc);
|
|
else if (sinfo != IP_NULL)
|
|
ipcom_ipc_free(sinfo);
|
|
|
|
if (shell_fd == IP_INVALID_SOCKET)
|
|
return IPCOM_ERR_FAILED;
|
|
else
|
|
{
|
|
int enable = IP_TRUE;
|
|
|
|
*stdio_sock = shell_fd;
|
|
#if IPCOM_USE_SOCK != IPCOM_SOCK_IPLITE
|
|
/*
|
|
* Disable Nagle (enable no delay) on this socket since it is an
|
|
* interactive connection
|
|
*/
|
|
(void)ipcom_setsockopt(shell_fd, IP_IPPROTO_TCP, IP_TCP_NODELAY, &enable, sizeof(enable));
|
|
#endif
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_shell_exit
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipssh_shell_exit(void* ssh_ctx, void* shell_ctx)
|
|
{
|
|
Ipssh_c_api_ctx* proxy_ctx = ssh_ctx;
|
|
|
|
(void)shell_ctx;
|
|
|
|
if (ipcom_shutdown(proxy_ctx->stdio_sock, IP_SHUT_RDWR))
|
|
return IPCOM_ERR_FAILED;
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_send_shell_stdout_data
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipssh_send_shell_stdout_data(void* ssh_ctx, void* shell_ctx, const char* data, Ip_ssize_t len)
|
|
{
|
|
Ipssh_c_api_ctx* proxy_ctx = ssh_ctx;
|
|
|
|
(void)shell_ctx;
|
|
|
|
if (ipcom_socketwrite(proxy_ctx->stdio_sock, data, len) != len)
|
|
return -1;
|
|
|
|
return (int)len;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipssh_get_clt_fd
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_fd
|
|
ipssh_get_clt_fd(void* ssh_ctx)
|
|
{
|
|
Ipssh_c_api_ctx* proxy_ctx = ssh_ctx;
|
|
|
|
return proxy_ctx->peer_sock;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|