/***************************************************************************** * file lib/management/cmd_SSH2.c * author YuLiang * version 1.0.0 * date 20-Mar-2025 * brief This file provides all the SSH2 cmd related operation functions. ****************************************************************************** * Attention * *

© COPYRIGHT(c) 2025 LandPower

* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of LandPower nor the names of its contributors may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* 标准C库头文件. */ #include #include #include #include /* 用户代码头文件. */ #include "main.h" #include "vty.h" #include "cmd.h" #include "mtimer.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ #define VTYCMD_PORT "2222" #define VTYCMD_RSA_PATH "/home/root/ssh/ssh_host_rsa_key" #define VTYCMD_HOSTKEY_PATH "/home/root/ssh/ssh_host_ed25519_key" #define VTYCMD_BUF_OUT_LEN 32768 typedef struct _vtycmd_ctrl { vty_t *vtycmd; ssh_bind sshbind; ssh_session session; ssh_channel channel; struct ssh_server_callbacks_struct server_cb; struct ssh_channel_callbacks_struct channel_cb; uint8_t is_connect; uint8_t is_recv; uint16_t cmd_idx; char cmd[VTY_BUFSIZ]; uint16_t out_buf_start; uint16_t out_buf_end; char out_buf[VTYCMD_BUF_OUT_LEN]; } vtycmd_ctrl_t; /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* cmd vty结构体. */ static vtycmd_ctrl_t vtycmd_ctrl; /* Private function prototypes -----------------------------------------------*/ /* 在终端上显示当前配置信息. */ CMD(show_sshcmd, show_sshcmd_cmd, "show sshcmd", SHOW_STR "SSHCMD\n") { vty_out(vty, "Connect: %s\r\n\n", vtycmd_ctrl.is_connect ? "yes" : "no"); return CMD_SUCCESS; } #if 0 /* Insert a word into vty interface with overwrite mode. */ void _vtycmd_insert_word(char *str) { int len = strlen(str); ssh_channel_write(vtycmd_ctrl.channel, str, len); strcpy(&vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx], str); vtycmd_ctrl.cmd_idx += len; vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0; } /* commandLine '?'响应函数. */ int _vtycmd_question(void) { array_t *cmd_line = NULL; cmd_line = cmd_strs_create(vtycmd_ctrl.cmd); if (NULL == cmd_line) { cmd_line = array_init(1, MTYPE_CLI); array_append(cmd_line, '\0', MTYPE_CLI); } else if (vtycmd_ctrl.cmd_idx && isspace((int)vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx - 1])) array_append(cmd_line, '\0', MTYPE_CLI); vty_question(vtycmd_ctrl.vtycmd, cmd_line); cmd_strs_free(cmd_line); vty_prompt(vtycmd_ctrl.vtycmd); vty_out(vtycmd_ctrl.vtycmd, "%s", vtycmd_ctrl.cmd); return 0; } /* 用于自动补齐命令行关键字. */ void _vtycmd_completion_append(array_t *cmd_line, char *word, int32_t status) { uint32_t index = array_active(cmd_line) - 1; uint32_t len = 0; uint32_t i = 0; if (NULL == array_get(cmd_line, index)) { _vtycmd_insert_word(word); } else { len = strlen(array_get(cmd_line, index)); for(i = 0; i < len; i++) { if (vtycmd_ctrl.cmd_idx) { vtycmd_ctrl.cmd_idx--; vty_out(vtycmd_ctrl.vtycmd, "%c%c%c", 8, ' ', 8); } } _vtycmd_insert_word(word); } if (CMD_COMPLETE_FULL_MATCH == status) _vtycmd_insert_word(" "); } /* commandLine 'TAB'响应函数. */ int _vtycmd_completion(void) { array_t *cmd_line = NULL; char **match_strs = NULL; int32_t complete_status = CMD_ERR_NO_MATCH; cmd_line = cmd_strs_create(vtycmd_ctrl.cmd); if (NULL == cmd_line) { cmd_line = array_init(1, MTYPE_CLI); array_append(cmd_line, '\0', MTYPE_CLI); } else if (vtycmd_ctrl.cmd_idx && isspace((int)vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx - 1])) array_append(cmd_line, '\0', MTYPE_CLI); match_strs = cmd_complete_command(cmd_line, vtycmd_ctrl.vtycmd, &complete_status); if (NULL == match_strs) { cmd_strs_free(cmd_line); return 0; } if (CMD_COMPLETE_MATCH == complete_status || CMD_COMPLETE_FULL_MATCH == complete_status) _vtycmd_completion_append(cmd_line, match_strs[0], complete_status); else { vty_print_word(vtycmd_ctrl.vtycmd, match_strs); vty_prompt(vtycmd_ctrl.vtycmd); vty_out(vtycmd_ctrl.vtycmd, "%s", vtycmd_ctrl.cmd); } vty_free_match_strs(match_strs); cmd_strs_free(cmd_line); return 0; } /* Internal functions --------------------------------------------------------*/ /* Print command line history. This function is called from vty_next_line and vty_previous_line. */ static void _vtycmd_history_print(vty_t *vty) { uint16_t i= 0; for(i = 0; i < vtycmd_ctrl.cmd_idx; i++) { vty_out(vtycmd_ctrl.vtycmd, "%c%c%c", 8, ' ', 8); } vtycmd_ctrl.cmd_idx = 0; /* Get previous line from history buffer */ vtycmd_ctrl.cmd_idx = strlen(vty->hist[vty->hp]); memcpy(vtycmd_ctrl.cmd, vty->hist[vty->hp], vtycmd_ctrl.cmd_idx); vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0; /* Redraw current line */ vty_out(vtycmd_ctrl.vtycmd, "%s", vtycmd_ctrl.cmd); } /* Show previous command line history. */ static void _vtycmd_previous_line(vty_t *vty) { unsigned int try_index = 0; try_index = vty->hp; if (try_index == 0) try_index = VTY_MAXHIST - 1; else try_index--; if (vty->hist[try_index] == NULL) return; else vty->hp = try_index; _vtycmd_history_print(vty); } /* Show next command line history. */ static void _vtycmd_next_line(vty_t *vty) { unsigned int try_index = 0; if (vty->hp == vty->hindex) return; /* Try is there history exist or not. */ try_index = vty->hp; if (try_index == (VTY_MAXHIST - 1)) try_index = 0; else try_index++; /* If there is not history return. */ if (vty->hist[try_index] == NULL) return; else vty->hp = try_index; _vtycmd_history_print(vty); } int _vtycmd_cmd(char *buf, uint32_t len) { uint16_t i = 0; vty_t *vty = vtycmd_ctrl.vtycmd; for (i = 0; i < len; i++) { switch (buf[i]) { case '\033': if (i + 2 < len && buf[i + 1] == '[') { if (buf[i + 2] == 'A') { _vtycmd_previous_line(vty); } else if (buf[i + 2] == 'B') { _vtycmd_next_line(vty); } i += 2; } break; case CONTROL('H'): case 0x7f: if (vtycmd_ctrl.cmd_idx) { vtycmd_ctrl.cmd_idx--; vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0; vty_out(vty, "%c%c%c", 8, ' ', 8); } break; case '\n': case '\r': vty_out(vty, "%s", VTY_NEWLINE); /* 执行命令 */ vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0; vty->length = vtycmd_ctrl.cmd_idx; snprintf(vty->buf, VTY_BUFSIZ, "%s", vtycmd_ctrl.cmd); vty_execute(vty); vtycmd_ctrl.cmd_idx = 0; vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0; break; case '\t': _vtycmd_completion(); break; case '?': _vtycmd_question(); break; default: if (buf[i] > 31 && buf[i] < 127) { vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = buf[i]; vtycmd_ctrl.cmd_idx++; vtycmd_ctrl.cmd[vtycmd_ctrl.cmd_idx] = 0; vty_out(vty, "%c", buf[i]); } break; } } return E_NONE; } /* 客户端通讯相关的回调 */ int _vtycmd_channel_pty_request(ssh_session session, ssh_channel channel, const char *term, int cols, int rows, int py, int px, void *userdata) { ssh_channel_write(vtycmd_ctrl.channel, "Username: ", 10); ssh_channel_request_pty_size(channel, term, cols, rows); return SSH_OK; } int _vtycmd_channel_pty_resize(ssh_session session, ssh_channel channel, int cols, int rows, int py, int px, void *userdata) { return ssh_channel_change_pty_size(channel, cols, rows); } int _vtycmd_channel_shell_request(ssh_session session, ssh_channel channel, void *userdata) { return SSH_OK; } int _vtycmd_channel_exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata) { return ssh_channel_request_exec(channel, command); } int _vtycmd_channel_subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata) { return SSH_OK; } int _vtycmd_channel_env_request_function(ssh_session session, ssh_channel channel, const char *env_name, const char *env_value, void *userdata) { return ssh_channel_request_env(channel, env_name, env_value); } /* 数据处理 */ int _vtycmd_channel_data_function(ssh_session session, ssh_channel channel, void *data, uint32_t len, int is_stderr, void *userdata) { vtycmd_ctrl.is_recv = TRUE; _vtycmd_cmd((char*)data, len); return len; } void _vtycmd_channel_close(ssh_session session, ssh_channel channel, void *userdata) { vtycmd_ctrl.is_connect = FALSE; } /* 服务端认证通道相关回调 */ int _vtycmd_server_auth_pass(ssh_session session, const char *user, const char *password, void *userdata) { if (strcmp("root", user) == 0 && strcmp("123456", password) == 0) { return SSH_AUTH_SUCCESS; } else { return SSH_AUTH_DENIED; } } /* 空认证方法 */ int _vtycmd_server_auth_none_callback(ssh_session session, const char *user, void *userdata) { return SSH_AUTH_SUCCESS; } /* pubkey 认证方法 */ int _vtycmd_server_auth_pubkey(ssh_session session, const char *user, struct ssh_key_struct *pubkey, char signature_state, void *userdata) { return SSH_AUTH_SUCCESS; } int _vtycmd_server_request_callback(ssh_session session, const char *service, void *userdata) { return SSH_OK; } ssh_channel _vtycmd_server_open_channel(ssh_session session, void *userdata) { vtycmd_ctrl.channel = ssh_channel_new(session); ssh_callbacks_init(&vtycmd_ctrl.channel_cb); ssh_set_channel_callbacks(vtycmd_ctrl.channel, &vtycmd_ctrl.channel_cb); return vtycmd_ctrl.channel; } /* SSH2 命令行主函数 */ void *_vtycmd_handle(void *arg) { /* 初始化 libssh */ if (ssh_init() < 0) { log_err(LOG_CLI, "ssh_init failed\r\n"); return NULL; } /* 初始化结构体 */ vtycmd_ctrl.server_cb.auth_password_function = _vtycmd_server_auth_pass; vtycmd_ctrl.server_cb.auth_none_function = _vtycmd_server_auth_none_callback; vtycmd_ctrl.server_cb.auth_pubkey_function = _vtycmd_server_auth_pubkey; vtycmd_ctrl.server_cb.service_request_function = _vtycmd_server_request_callback; vtycmd_ctrl.server_cb.channel_open_request_session_function = _vtycmd_server_open_channel; vtycmd_ctrl.channel_cb.channel_data_function = _vtycmd_channel_data_function; vtycmd_ctrl.channel_cb.channel_close_function = _vtycmd_channel_close; vtycmd_ctrl.channel_cb.channel_pty_request_function = _vtycmd_channel_pty_request; vtycmd_ctrl.channel_cb.channel_shell_request_function = _vtycmd_channel_shell_request; vtycmd_ctrl.channel_cb.channel_pty_window_change_function = _vtycmd_channel_pty_resize; vtycmd_ctrl.channel_cb.channel_exec_request_function = _vtycmd_channel_exec_request; vtycmd_ctrl.channel_cb.channel_env_request_function = _vtycmd_channel_env_request_function; vtycmd_ctrl.channel_cb.channel_subsystem_request_function = _vtycmd_channel_subsystem_request; /* 创建 SSH2 接口 */ vtycmd_ctrl.sshbind = ssh_bind_new(); ssh_bind_options_set(vtycmd_ctrl.sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, VTYCMD_PORT); ssh_bind_options_set(vtycmd_ctrl.sshbind, SSH_BIND_OPTIONS_RSAKEY, VTYCMD_RSA_PATH); ssh_bind_options_set(vtycmd_ctrl.sshbind, SSH_BIND_OPTIONS_HOSTKEY, VTYCMD_HOSTKEY_PATH); if (ssh_bind_listen(vtycmd_ctrl.sshbind) < 0) { log_err(LOG_CLI, "Error listening to socket: %s", ssh_get_error(vtycmd_ctrl.sshbind)); return NULL; } while(1) { /* 等待连接 */ ssh_session session = ssh_new(); if (ssh_bind_accept(vtycmd_ctrl.sshbind, session) != SSH_OK) { DBG(DBG_M_CLI, "Error accept to socket: %s\n", ssh_get_error(vtycmd_ctrl.sshbind)); continue; } /* 只能接收一个连接 */ if (vtycmd_ctrl.is_connect) { ssh_disconnect(session); ssh_free(session); continue; } /* 初始化连接 */ log_notice(LOG_CLI, "SSH2 command is connect.\n"); vtycmd_ctrl.is_connect = TRUE; ssh_callbacks_init(&vtycmd_ctrl.server_cb); ssh_set_server_callbacks(session, &vtycmd_ctrl.server_cb); ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD); if (ssh_handle_key_exchange(session) != SSH_OK) { DBG(DBG_M_CLI, "Error performing key exchange: %s\n", ssh_get_error(session)); ssh_disconnect(session); ssh_free(session); continue; } ssh_set_blocking(session, 0); ssh_event event = ssh_event_new(); ssh_event_add_session(event, session); /* 处理连接断开 */ while(1) { if (ssh_event_dopoll(event, 3000) == SSH_ERROR || !vtycmd_ctrl.is_connect) { /* 终端断开处理 */ log_notice(LOG_CLI, "SSH2 command is disconnect.\n"); ssh_channel_close(vtycmd_ctrl.channel); ssh_channel_free(vtycmd_ctrl.channel); ssh_disconnect(session); ssh_event_free(event); ssh_free(session); vtycmd_ctrl.is_connect = FALSE; if (CONFIG_NODE == vtycmd_ctrl.vtycmd->node) { vty_config_unlock(vtycmd_ctrl.vtycmd); } vtycmd_ctrl.vtycmd->node = USERNAME_NODE; break; } } } return NULL; } /* 用于判断终端是否超时 */ void* _vtycmd_timer(void *arg) { if (!vtycmd_ctrl.is_recv && vtycmd_ctrl.is_connect) { vtycmd_ctrl.is_connect = FALSE; } vtycmd_ctrl.is_recv = FALSE; /* 重新加入定时器. */ mtimer_add(_vtycmd_timer, NULL, 120, "VTYCMD_TIMER"); return NULL; } /* SSH2 命令行发送数据 */ void *_vtycmd_send_handle(void *arg) { char *buf = NULL; uint16_t end = 0; while(1) { usleep(100000); if (!vtycmd_ctrl.is_connect) { continue; } end = vtycmd_ctrl.out_buf_end; buf = vtycmd_ctrl.out_buf + vtycmd_ctrl.out_buf_start; if (end == vtycmd_ctrl.out_buf_start) { continue; } if (end < vtycmd_ctrl.out_buf_start) { ssh_channel_write(vtycmd_ctrl.channel, buf, VTYCMD_BUF_OUT_LEN - vtycmd_ctrl.out_buf_start); vtycmd_ctrl.out_buf_start = 0; } else { ssh_channel_write(vtycmd_ctrl.channel, buf, end - vtycmd_ctrl.out_buf_start); vtycmd_ctrl.out_buf_start = end; } } return NULL; } #endif /* Interface functions -------------------------------------------------------*/ /* 初始化 SSH2 命令行 */ void vtycmd_init(void) { /* Make vty structure. */ vtycmd_ctrl.vtycmd = vty_create(); vtycmd_ctrl.vtycmd->type = VTY_CMD; vtycmd_ctrl.vtycmd->node = USERNAME_NODE; memset(vtycmd_ctrl.vtycmd->hist, 0, sizeof(vtycmd_ctrl.vtycmd->hist)); vtycmd_ctrl.vtycmd->hp = 0; vtycmd_ctrl.vtycmd->hindex = 0; cmd_install_element(COMMON_NODE, &show_sshcmd_cmd); } /* 启动 SSH2 命令行 */ void vtycmd_cmd_init(void) { return ; #if 0 pthread_t pid; struct sched_param param; pthread_attr_t attr; int32_t rv = 0; /* 配置线程RR调度, 优先级75 */ pthread_attr_init(&attr); param.sched_priority = 75; pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); rv = pthread_create(&pid, &attr, _vtycmd_handle, NULL); if (rv != 0) { log_err(LOG_DEFAULT, "vtycmd_cmd_init can't create pthread %d!", rv); } else { thread_m_add("CMD", pid); } pthread_attr_destroy(&attr); mtimer_add(_vtycmd_timer, NULL, 120, "VTYCMD_TIMER"); /* 配置线程RR调度, 优先级75 */ pthread_attr_init(&attr); param.sched_priority = 75; pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); rv = pthread_create(&pid, &attr, _vtycmd_send_handle, NULL); if (rv != 0) { log_err(LOG_DEFAULT, "vtycmd_cmd_init can't create pthread %d!", rv); } else { thread_m_add("CMD_SNED", pid); } pthread_attr_destroy(&attr); #endif } /* cmd 是否连接 */ bool vtycmd_connect(void) { return vtycmd_ctrl.is_connect; } /* 命令行输出 */ void vtycmd_print(const char *format, va_list va) { char *buf = vtycmd_ctrl.out_buf + vtycmd_ctrl.out_buf_end; char buf_temp[VTY_BUFSIZ]; char *buf_temp_p = buf_temp; uint16_t start = 0;; int len = 0; int len_remain = 0; int len_temp = 0; if (!vtycmd_ctrl.is_connect) { vtycmd_ctrl.out_buf_start = 0; vtycmd_ctrl.out_buf_end = 0; return; } len = vsnprintf(buf_temp, VTY_BUFSIZ, format, va); if (len <= 0) { return; } /* 计算可写入的数据量 */ start = vtycmd_ctrl.out_buf_start; if (vtycmd_ctrl.out_buf_end >= start) { len_remain = VTYCMD_BUF_OUT_LEN - 1 - (vtycmd_ctrl.out_buf_end - start); } else { len_remain = start - vtycmd_ctrl.out_buf_end - 1; } if (len_remain <= 0) { return; } if (len > len_remain) { len = len_remain; } /* 超过 out_buf 需要分段存储 */ if (len + vtycmd_ctrl.out_buf_end > VTYCMD_BUF_OUT_LEN) { len_temp = VTYCMD_BUF_OUT_LEN - vtycmd_ctrl.out_buf_end; memcpy(buf, buf_temp_p, len_temp); vtycmd_ctrl.out_buf_end = 0; buf = vtycmd_ctrl.out_buf; buf_temp_p = buf_temp_p + len_temp; len -= len_temp; memcpy(buf, buf_temp_p, len); vtycmd_ctrl.out_buf_end += len; } else { memcpy(buf, buf_temp_p, len); len_temp = vtycmd_ctrl.out_buf_end + len; if (VTYCMD_BUF_OUT_LEN == len_temp) { vtycmd_ctrl.out_buf_end = 0; } else { vtycmd_ctrl.out_buf_end = len_temp; } } } /************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/