/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipssh_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include "ipcom_clib.h" #include #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 **************************************************************************** */