From 3eb59eb0714b070c886f04cfce2298d6c324c967 Mon Sep 17 00:00:00 2001 From: Apex Liu Date: Fri, 4 May 2018 01:30:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=EF=BC=9A=E5=BC=BA=E5=88=B6?= =?UTF-8?q?=E4=B8=AD=E6=96=AD=E5=9C=A8=E7=BA=BF=E4=BC=9A=E8=AF=9D=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=AE=9E=E7=8E=B0=E4=BA=86=EF=BC=8CSSH=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E5=B7=B2=E6=B5=8B=E8=AF=95=EF=BC=8CRDP=E5=92=8CTELNET?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E5=B0=9A=E6=9C=AA=E5=AE=9E=E7=8E=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/tp_core/common/protocol_interface.h | 7 + server/tp_core/core/tp_tpp_mgr.cpp | 13 +- server/tp_core/core/tp_tpp_mgr.h | 3 + server/tp_core/core/ts_http_rpc.cpp | 1086 +++++++++-------- server/tp_core/core/ts_http_rpc.h | 2 + server/tp_core/protocol/ssh/ssh_proxy.cpp | 14 + server/tp_core/protocol/ssh/ssh_proxy.h | 1 + server/tp_core/protocol/ssh/ssh_session.cpp | 16 +- server/tp_core/protocol/ssh/ssh_session.h | 2 + server/tp_core/protocol/ssh/tpssh.cpp | 44 + .../tp_core/protocol/ssh/tpssh.vs2015.vcxproj | 8 +- .../protocol/ssh/tpssh.vs2015.vcxproj.filters | 341 +++--- .../teleport/static/js/audit/record-list.js | 2 +- .../teleport/static/js/ops/session-list.js | 79 +- server/www/teleport/view/ops/sessions.mako | 18 +- .../webroot/app/controller/__init__.py | 2 + .../teleport/webroot/app/controller/ops.py | 26 + .../www/teleport/webroot/app/model/record.py | 4 +- 18 files changed, 941 insertions(+), 727 deletions(-) diff --git a/server/tp_core/common/protocol_interface.h b/server/tp_core/common/protocol_interface.h index fc883f6..eb95e21 100644 --- a/server/tp_core/common/protocol_interface.h +++ b/server/tp_core/common/protocol_interface.h @@ -14,6 +14,9 @@ # define TPP_API #endif +#define TPP_CMD_INIT 0x00000000 +#define TPP_CMD_KILL_SESSIONS 0x00000006 + typedef struct TPP_CONNECT_INFO { char* sid; @@ -79,6 +82,8 @@ extern "C" TPP_API void tpp_timer(void); TPP_API void tpp_set_cfg(TPP_SET_CFG_ARGS* cfg_args); + TPP_API ex_rv tpp_command(ex_u32 cmd, const char* param); + #ifdef __cplusplus } #endif @@ -89,4 +94,6 @@ typedef ex_rv(*TPP_STOP_FUNC)(void); typedef void(*TPP_TIMER_FUNC)(void); typedef void(*TPP_SET_CFG_FUNC)(TPP_SET_CFG_ARGS* cfg_args); +typedef ex_rv(*TPP_COMMAND_FUNC)(ex_u32 cmd, const char* param); // param is a JSON formatted string. + #endif // __TP_PROTOCOL_INTERFACE_H__ diff --git a/server/tp_core/core/tp_tpp_mgr.cpp b/server/tp_core/core/tp_tpp_mgr.cpp index d3e1c64..27ac29f 100644 --- a/server/tp_core/core/tp_tpp_mgr.cpp +++ b/server/tp_core/core/tp_tpp_mgr.cpp @@ -47,15 +47,17 @@ bool TppManager::load_tpp(const ex_wstr& libname) lib->stop = (TPP_STOP_FUNC)GetProcAddress(lib->dylib, "tpp_stop"); lib->timer = (TPP_TIMER_FUNC)GetProcAddress(lib->dylib, "tpp_timer"); lib->set_cfg = (TPP_SET_CFG_FUNC)GetProcAddress(lib->dylib, "tpp_set_cfg"); + lib->command = (TPP_COMMAND_FUNC)GetProcAddress(lib->dylib, "tpp_command"); #else lib->init = (TPP_INIT_FUNC)dlsym(lib->dylib, "tpp_init"); lib->start = (TPP_START_FUNC)dlsym(lib->dylib, "tpp_start"); lib->stop = (TPP_STOP_FUNC)dlsym(lib->dylib, "tpp_stop"); lib->timer = (TPP_TIMER_FUNC)dlsym(lib->dylib, "tpp_timer"); lib->set_cfg = (TPP_SET_CFG_FUNC)dlsym(lib->dylib, "tpp_set_cfg"); + lib->command = (TPP_COMMAND_FUNC)dlsym(lib->dylib, "tpp_command"); #endif - if (lib->init == NULL || lib->start == NULL || lib->stop == NULL || lib->timer == NULL || lib->set_cfg == NULL) + if (lib->init == NULL || lib->start == NULL || lib->stop == NULL || lib->timer == NULL || lib->set_cfg == NULL || lib->command == NULL) { EXLOGE(L"[core] load dylib `%ls` failed, can not locate all functions.\n", libfile.c_str()); delete lib; @@ -118,3 +120,12 @@ void TppManager::set_config(int noop_timeout) { (*it)->set_cfg(&args); } } + +void TppManager::kill_sessions(const ex_astr& sessions) { + tpp_libs::iterator it = m_libs.begin(); + for (; it != m_libs.end(); ++it) + { + (*it)->command(TPP_CMD_KILL_SESSIONS, sessions.c_str()); + } +} + diff --git a/server/tp_core/core/tp_tpp_mgr.h b/server/tp_core/core/tp_tpp_mgr.h index 9c88119..cec6d47 100644 --- a/server/tp_core/core/tp_tpp_mgr.h +++ b/server/tp_core/core/tp_tpp_mgr.h @@ -25,6 +25,8 @@ typedef struct TPP_LIB TPP_STOP_FUNC stop; TPP_TIMER_FUNC timer; TPP_SET_CFG_FUNC set_cfg; + + TPP_COMMAND_FUNC command; }TPP_LIB; typedef std::list tpp_libs; @@ -51,6 +53,7 @@ public: int count(void) { return m_libs.size(); } void set_config(int noop_timeout); + void kill_sessions(const ex_astr& sessions); private: tpp_libs m_libs; diff --git a/server/tp_core/core/ts_http_rpc.cpp b/server/tp_core/core/ts_http_rpc.cpp index 696acac..c5d1f4b 100644 --- a/server/tp_core/core/ts_http_rpc.cpp +++ b/server/tp_core/core/ts_http_rpc.cpp @@ -1,523 +1,565 @@ -#include "ts_http_rpc.h" -#include "ts_ver.h" -#include "ts_env.h" -#include "ts_session.h" -#include "ts_crypto.h" -#include "ts_web_rpc.h" -#include "tp_tpp_mgr.h" - -extern TppManager g_tpp_mgr; - -#include - - -#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') -int ts_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) -{ - int i, j, a, b; - - for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) - { - if (src[i] == '%') - { - if (i < src_len - 2 && isxdigit(*(const unsigned char *)(src + i + 1)) && - isxdigit(*(const unsigned char *)(src + i + 2))) { - a = tolower(*(const unsigned char *)(src + i + 1)); - b = tolower(*(const unsigned char *)(src + i + 2)); - dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); - i += 2; - } - else - { - return -1; - } - } - else if (is_form_url_encoded && src[i] == '+') - { - dst[j] = ' '; - } - else - { - dst[j] = src[i]; - } - } - - dst[j] = '\0'; /* Null-terminate the destination */ - - return i >= src_len ? j : -1; -} - -TsHttpRpc::TsHttpRpc() : - ExThreadBase("http-rpc-thread") -{ - mg_mgr_init(&m_mg_mgr, NULL); -} - -TsHttpRpc::~TsHttpRpc() -{ - mg_mgr_free(&m_mg_mgr); -} - -void TsHttpRpc::_thread_loop(void) -{ - EXLOGI("[core] TeleportServer-RPC ready on %s:%d\n", m_host_ip.c_str(), m_host_port); - - while (!m_stop_flag) - { - mg_mgr_poll(&m_mg_mgr, 500); - } - - EXLOGV("[core] rpc main loop end.\n"); -} - -void TsHttpRpc::_set_stop_flag(void) -{ - m_stop_flag = true; -} - -bool TsHttpRpc::init(void) -{ - struct mg_connection* nc = NULL; - - m_host_ip = g_env.rpc_bind_ip; - m_host_port = g_env.rpc_bind_port; - - char addr[128] = { 0 }; - // if (0 == strcmp(m_host_ip.c_str(), "127.0.0.1") || 0 == strcmp(m_host_ip.c_str(), "localhost")) - // ex_strformat(addr, 128, ":%d", m_host_port); - // else - // ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); - if (0 == strcmp(m_host_ip.c_str(), "0.0.0.0")) - ex_strformat(addr, 128, ":%d", m_host_port); - else - ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); - - nc = mg_bind(&m_mg_mgr, addr, _mg_event_handler); - if (NULL == nc) - { - EXLOGE("[core] rpc listener failed to bind at %s.\n", addr); - return false; - } - - nc->user_data = this; - - mg_set_protocol_http_websocket(nc); - - // 导致内存泄露的地方(每次请求约消耗1KB内存) - // DO NOT USE MULTITHREADING OF MG. - // cpq (one of the authors of MG) commented on 3 Feb: Multithreading support has been removed. - // https://github.com/cesanta/mongoose/commit/707b9ed2d6f177b3ad8787cb16a1bff90ddad992 - //mg_enable_multithreading(nc); - - return true; -} - -void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_data) -{ - struct http_message *hm = (struct http_message*)ev_data; - - TsHttpRpc* _this = (TsHttpRpc*)nc->user_data; - if (NULL == _this) - { - EXLOGE("[core] rpc invalid http request.\n"); - return; - } - - switch (ev) - { - case MG_EV_HTTP_REQUEST: - { - ex_astr ret_buf; - - ex_astr uri; - uri.assign(hm->uri.p, hm->uri.len); - - //EXLOGD("[core] rpc got request: %s\n", uri.c_str()); - - if (uri == "/rpc") - { - ex_astr method; - Json::Value json_param; - - ex_rv rv = _this->_parse_request(hm, method, json_param); - if (TPE_OK != rv) - { - EXLOGE("[core] rpc got invalid request.\n"); - _this->_create_json_ret(ret_buf, rv); - } - else - { - EXLOGD("[core] rpc got request method `%s`\n", method.c_str()); - _this->_process_request(method, json_param, ret_buf); - } - } - else - { - EXLOGE("[core] rpc got invalid request: not `rpc` uri.\n"); - _this->_create_json_ret(ret_buf, TPE_PARAM, "not a `rpc` request."); - } - - mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: application/json\r\n\r\n%s", (int)ret_buf.size() - 1, &ret_buf[0]); - nc->flags |= MG_F_SEND_AND_CLOSE; - } - break; - default: - break; - } -} - -ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Json::Value& json_param) -{ - if (NULL == req) - return TPE_PARAM; - - bool is_get = true; - if (req->method.len == 3 && 0 == memcmp(req->method.p, "GET", req->method.len)) - is_get = true; - else if (req->method.len == 4 && 0 == memcmp(req->method.p, "POST", req->method.len)) - is_get = false; - else - return TPE_HTTP_METHOD; - - ex_astr json_str; - if (is_get) - json_str.assign(req->query_string.p, req->query_string.len); - else - json_str.assign(req->body.p, req->body.len); - - if (0 == json_str.length()) - return TPE_PARAM; - - // 将参数进行 url-decode 解码 - int len = json_str.length() * 2; - ex_chars sztmp; - sztmp.resize(len); - memset(&sztmp[0], 0, len); - if (-1 == ts_url_decode(json_str.c_str(), json_str.length(), &sztmp[0], len, 0)) - return TPE_HTTP_URL_ENCODE; - - json_str = &sztmp[0]; - - Json::Reader jreader; - - if (!jreader.parse(json_str.c_str(), json_param)) - return TPE_JSON_FORMAT; - - if (json_param.isArray()) - return TPE_PARAM; - - if (json_param["method"].isNull() || !json_param["method"].isString()) - return TPE_PARAM; - - func_cmd = json_param["method"].asCString(); - json_param = json_param["param"]; - - return TPE_OK; -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const Json::Value& jr_data) -{ - // 返回: {"code":errcode, "data":{jr_data}} - - Json::FastWriter jr_writer; - Json::Value jr_root; - - jr_root["code"] = errcode; - jr_root["data"] = jr_data; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) -{ - // 返回: {"code":errcode} - - Json::FastWriter jr_writer; - Json::Value jr_root; - - jr_root["code"] = errcode; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const char* message) -{ - // 返回: {"code":errcode, "message":message} - - Json::FastWriter jr_writer; - Json::Value jr_root; - - jr_root["code"] = errcode; - jr_root["message"] = message; - buf = jr_writer.write(jr_root); -} - -void TsHttpRpc::_process_request(const ex_astr& func_cmd, const Json::Value& json_param, ex_astr& buf) -{ - if (func_cmd == "request_session") { - _rpc_func_request_session(json_param, buf); - } - else if (func_cmd == "get_config") { - _rpc_func_get_config(json_param, buf); - } - else if (func_cmd == "set_config") { - _rpc_func_set_config(json_param, buf); - } - else if (func_cmd == "enc") { - _rpc_func_enc(json_param, buf); - } - else if (func_cmd == "exit") { - _rpc_func_exit(json_param, buf); - } - else { - EXLOGE("[core] rpc got unknown command: %s\n", func_cmd.c_str()); - _create_json_ret(buf, TPE_UNKNOWN_CMD); - } -} - -extern bool g_exit_flag; // 要求整个TS退出的标志(用于停止各个工作线程) -void TsHttpRpc::_rpc_func_exit(const Json::Value& json_param, ex_astr& buf) -{ - // 设置一个全局退出标志 - g_exit_flag = true; - _create_json_ret(buf, TPE_OK); -} - -void TsHttpRpc::_rpc_func_get_config(const Json::Value& json_param, ex_astr& buf) -{ - Json::Value jr_data; - - ex_astr _replay_name; - ex_wstr2astr(g_env.m_replay_path, _replay_name); - jr_data["replay-path"] = _replay_name; - - jr_data["web-server-rpc"] = g_env.web_server_rpc; - - ex_astr _version; - ex_wstr2astr(TP_SERVER_VER, _version); - jr_data["version"] = _version; - - ExIniFile& ini = g_env.get_ini(); - ex_ini_sections& secs = ini.GetAllSections(); - ex_ini_sections::iterator it = secs.begin(); - for (; it != secs.end(); ++it) - { - if (it->first.length() > 9 && 0 == wcsncmp(it->first.c_str(), L"protocol-", 9)) - { - ex_wstr name; - name.assign(it->first, 9, it->first.length() - 9); - ex_astr _name; - ex_wstr2astr(name, _name); - - bool enabled = false; - it->second->GetBool(L"enabled", enabled, false); - - ex_wstr ip; - if (!it->second->GetStr(L"bind-ip", ip)) - continue; - ex_astr _ip; - ex_wstr2astr(ip, _ip); - - int port; - it->second->GetInt(L"bind-port", port, 52189); - - jr_data[_name.c_str()]["enable"] = enabled; - jr_data[_name.c_str()]["ip"] = _ip; - jr_data[_name.c_str()]["port"] = port; - } - } - - _create_json_ret(buf, TPE_OK, jr_data); -} - -void TsHttpRpc::_rpc_func_request_session(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#request_session - - int conn_id = 0; - ex_rv rv = TPE_OK; - - if (json_param["conn_id"].isNull()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - if (!json_param["conn_id"].isInt()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - conn_id = json_param["conn_id"].asInt(); - if (0 == conn_id) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - TS_CONNECT_INFO* info = new TS_CONNECT_INFO; - if ((rv = ts_web_rpc_get_conn_info(conn_id, *info)) != TPE_OK) - { - _create_json_ret(buf, rv); - return; - } - -// info->ref_count = 0; -// info->ticket_start = ex_get_tick_count(); -// - // 生成一个session-id(内部会避免重复) - ex_astr sid; - if (!g_session_mgr.request_session(sid, info)) { - _create_json_ret(buf, TPE_FAILED); - return; - } - - EXLOGD("[core] rpc new session-id: %s\n", sid.c_str()); - - Json::Value jr_data; - jr_data["sid"] = sid; - - _create_json_ret(buf, TPE_OK, jr_data); -} - -void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc - // 加密一个字符串 [ p=plain-text, c=cipher-text ] - // 入参: {"p":"need be encrypt"} - // 示例: {"p":"this-is-a-password"} - // p: 被加密的字符串 - // 返回: - // data域中的"c"的内容是加密后密文的base64编码结果 - // 示例: {"code":0, "data":{"c":"Mxs340a9r3fs+3sdf=="}} - // 错误返回: {"code":1234} - - if (json_param.isArray()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - ex_astr plain_text; - - if (json_param["p"].isNull() || !json_param["p"].isString()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - plain_text = json_param["p"].asCString(); - if (plain_text.length() == 0) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - ex_astr cipher_text; - - if (!ts_db_field_encrypt(plain_text, cipher_text)) - { - _create_json_ret(buf, TPE_FAILED); - return; - } - - Json::Value jr_data; - jr_data["c"] = cipher_text; - _create_json_ret(buf, TPE_OK, jr_data); -} - -void TsHttpRpc::_rpc_func_set_config(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#set_config - /* +#include "ts_http_rpc.h" +#include "ts_ver.h" +#include "ts_env.h" +#include "ts_session.h" +#include "ts_crypto.h" +#include "ts_web_rpc.h" +#include "tp_tpp_mgr.h" + +extern TppManager g_tpp_mgr; + +#include + + +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') +int ts_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded) +{ + int i, j, a, b; + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { - "noop-timeout": 900 # 900s = 15m - } - */ - - if (json_param.isArray()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - if (json_param["noop_timeout"].isNull() || !json_param["noop_timeout"].isUInt()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - int noop_timeout = json_param["noop_timeout"].asUInt(); - if (noop_timeout == 0) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - //static TppManager g_tpp_mgr; - EXLOGV("[core] no-op timeout set to %d minutes.\n", noop_timeout); - g_tpp_mgr.set_config(noop_timeout * 60); - - -// Json::Value jr_data; -// jr_data["c"] = cipher_text; -// _create_json_ret(buf, TPE_OK, jr_data); - _create_json_ret(buf, TPE_OK); -} - - -/* -void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) -{ - // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc - // 加密多个个字符串 [ p=plain-text, c=cipher-text ] - // 入参: {"p":["need be encrypt", "plain to cipher"]} - // 示例: {"p":["password-for-A"]} - // p: 被加密的字符串 - // 返回: - // data域中的"c"的内容是加密后密文的base64编码结果 - // 示例: {"code":0, "data":{"c":["Mxs340a9r3fs+3sdf=="]}} - // 错误返回: {"code":1234} - - if (json_param.isArray()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - ex_astr plain_text; - - if (json_param["p"].isNull() || !json_param["p"].isArray()) - { - _create_json_ret(buf, TPE_PARAM); - return; - } - - Json::Value c; - - Json::Value p = json_param["p"]; - int cnt = p.size(); - for (int i = 0; i < cnt; ++i) - { - if (!p[i].isString()) { - _create_json_ret(buf, TPE_PARAM); - return; - } - - ex_astr p_txt = p[i].asCString(); - if (p_txt.length() == 0) { - c["c"].append(""); - } - - ex_astr c_txt; - if (!ts_db_field_encrypt(p_txt, c_txt)) - { - _create_json_ret(buf, TPE_FAILED); - return; - } - - c["c"].append(c_txt); - } - - Json::Value jr_data; - jr_data["c"] = c; - _create_json_ret(buf, TPE_OK, jr_data); -} -*/ + if (src[i] == '%') + { + if (i < src_len - 2 && isxdigit(*(const unsigned char *)(src + i + 1)) && + isxdigit(*(const unsigned char *)(src + i + 2))) { + a = tolower(*(const unsigned char *)(src + i + 1)); + b = tolower(*(const unsigned char *)(src + i + 2)); + dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } + else + { + return -1; + } + } + else if (is_form_url_encoded && src[i] == '+') + { + dst[j] = ' '; + } + else + { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +TsHttpRpc::TsHttpRpc() : + ExThreadBase("http-rpc-thread") +{ + mg_mgr_init(&m_mg_mgr, NULL); +} + +TsHttpRpc::~TsHttpRpc() +{ + mg_mgr_free(&m_mg_mgr); +} + +void TsHttpRpc::_thread_loop(void) +{ + EXLOGI("[core] TeleportServer-RPC ready on %s:%d\n", m_host_ip.c_str(), m_host_port); + + while (!m_stop_flag) + { + mg_mgr_poll(&m_mg_mgr, 500); + } + + EXLOGV("[core] rpc main loop end.\n"); +} + +void TsHttpRpc::_set_stop_flag(void) +{ + m_stop_flag = true; +} + +bool TsHttpRpc::init(void) +{ + struct mg_connection* nc = NULL; + + m_host_ip = g_env.rpc_bind_ip; + m_host_port = g_env.rpc_bind_port; + + char addr[128] = { 0 }; + // if (0 == strcmp(m_host_ip.c_str(), "127.0.0.1") || 0 == strcmp(m_host_ip.c_str(), "localhost")) + // ex_strformat(addr, 128, ":%d", m_host_port); + // else + // ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); + if (0 == strcmp(m_host_ip.c_str(), "0.0.0.0")) + ex_strformat(addr, 128, ":%d", m_host_port); + else + ex_strformat(addr, 128, "%s:%d", m_host_ip.c_str(), m_host_port); + + nc = mg_bind(&m_mg_mgr, addr, _mg_event_handler); + if (NULL == nc) + { + EXLOGE("[core] rpc listener failed to bind at %s.\n", addr); + return false; + } + + nc->user_data = this; + + mg_set_protocol_http_websocket(nc); + + // 导致内存泄露的地方(每次请求约消耗1KB内存) + // DO NOT USE MULTITHREADING OF MG. + // cpq (one of the authors of MG) commented on 3 Feb: Multithreading support has been removed. + // https://github.com/cesanta/mongoose/commit/707b9ed2d6f177b3ad8787cb16a1bff90ddad992 + //mg_enable_multithreading(nc); + + return true; +} + +void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_data) +{ + struct http_message *hm = (struct http_message*)ev_data; + + TsHttpRpc* _this = (TsHttpRpc*)nc->user_data; + if (NULL == _this) + { + EXLOGE("[core] rpc invalid http request.\n"); + return; + } + + switch (ev) + { + case MG_EV_HTTP_REQUEST: + { + ex_astr ret_buf; + + ex_astr uri; + uri.assign(hm->uri.p, hm->uri.len); + + //EXLOGD("[core] rpc got request: %s\n", uri.c_str()); + + if (uri == "/rpc") + { + ex_astr method; + Json::Value json_param; + + ex_rv rv = _this->_parse_request(hm, method, json_param); + if (TPE_OK != rv) + { + EXLOGE("[core] rpc got invalid request.\n"); + _this->_create_json_ret(ret_buf, rv); + } + else + { + EXLOGD("[core] rpc got request method `%s`\n", method.c_str()); + _this->_process_request(method, json_param, ret_buf); + } + } + else + { + EXLOGE("[core] rpc got invalid request: not `rpc` uri.\n"); + _this->_create_json_ret(ret_buf, TPE_PARAM, "not a `rpc` request."); + } + + mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: application/json\r\n\r\n%s", (int)ret_buf.size() - 1, &ret_buf[0]); + nc->flags |= MG_F_SEND_AND_CLOSE; + } + break; + default: + break; + } +} + +ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Json::Value& json_param) +{ + if (NULL == req) + return TPE_PARAM; + + bool is_get = true; + if (req->method.len == 3 && 0 == memcmp(req->method.p, "GET", req->method.len)) + is_get = true; + else if (req->method.len == 4 && 0 == memcmp(req->method.p, "POST", req->method.len)) + is_get = false; + else + return TPE_HTTP_METHOD; + + ex_astr json_str; + if (is_get) + json_str.assign(req->query_string.p, req->query_string.len); + else + json_str.assign(req->body.p, req->body.len); + + if (0 == json_str.length()) + return TPE_PARAM; + + // 将参数进行 url-decode 解码 + int len = json_str.length() * 2; + ex_chars sztmp; + sztmp.resize(len); + memset(&sztmp[0], 0, len); + if (-1 == ts_url_decode(json_str.c_str(), json_str.length(), &sztmp[0], len, 0)) + return TPE_HTTP_URL_ENCODE; + + json_str = &sztmp[0]; + + Json::Reader jreader; + + if (!jreader.parse(json_str.c_str(), json_param)) + return TPE_JSON_FORMAT; + + if (json_param.isArray()) + return TPE_PARAM; + + if (json_param["method"].isNull() || !json_param["method"].isString()) + return TPE_PARAM; + + func_cmd = json_param["method"].asCString(); + json_param = json_param["param"]; + + return TPE_OK; +} + +void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const Json::Value& jr_data) +{ + // 返回: {"code":errcode, "data":{jr_data}} + + Json::FastWriter jr_writer; + Json::Value jr_root; + + jr_root["code"] = errcode; + jr_root["data"] = jr_data; + buf = jr_writer.write(jr_root); +} + +void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode) +{ + // 返回: {"code":errcode} + + Json::FastWriter jr_writer; + Json::Value jr_root; + + jr_root["code"] = errcode; + buf = jr_writer.write(jr_root); +} + +void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const char* message) +{ + // 返回: {"code":errcode, "message":message} + + Json::FastWriter jr_writer; + Json::Value jr_root; + + jr_root["code"] = errcode; + jr_root["message"] = message; + buf = jr_writer.write(jr_root); +} + +void TsHttpRpc::_process_request(const ex_astr& func_cmd, const Json::Value& json_param, ex_astr& buf) +{ + if (func_cmd == "request_session") { + _rpc_func_request_session(json_param, buf); + } + else if (func_cmd == "kill_sessions") { + _rpc_func_kill_sessions(json_param, buf); + } + else if (func_cmd == "get_config") { + _rpc_func_get_config(json_param, buf); + } + else if (func_cmd == "set_config") { + _rpc_func_set_config(json_param, buf); + } + else if (func_cmd == "enc") { + _rpc_func_enc(json_param, buf); + } + else if (func_cmd == "exit") { + _rpc_func_exit(json_param, buf); + } + else { + EXLOGE("[core] rpc got unknown command: %s\n", func_cmd.c_str()); + _create_json_ret(buf, TPE_UNKNOWN_CMD); + } +} + +extern bool g_exit_flag; // 要求整个TS退出的标志(用于停止各个工作线程) +void TsHttpRpc::_rpc_func_exit(const Json::Value& json_param, ex_astr& buf) +{ + // 设置一个全局退出标志 + g_exit_flag = true; + _create_json_ret(buf, TPE_OK); +} + +void TsHttpRpc::_rpc_func_get_config(const Json::Value& json_param, ex_astr& buf) +{ + Json::Value jr_data; + + ex_astr _replay_name; + ex_wstr2astr(g_env.m_replay_path, _replay_name); + jr_data["replay-path"] = _replay_name; + + jr_data["web-server-rpc"] = g_env.web_server_rpc; + + ex_astr _version; + ex_wstr2astr(TP_SERVER_VER, _version); + jr_data["version"] = _version; + + ExIniFile& ini = g_env.get_ini(); + ex_ini_sections& secs = ini.GetAllSections(); + ex_ini_sections::iterator it = secs.begin(); + for (; it != secs.end(); ++it) + { + if (it->first.length() > 9 && 0 == wcsncmp(it->first.c_str(), L"protocol-", 9)) + { + ex_wstr name; + name.assign(it->first, 9, it->first.length() - 9); + ex_astr _name; + ex_wstr2astr(name, _name); + + bool enabled = false; + it->second->GetBool(L"enabled", enabled, false); + + ex_wstr ip; + if (!it->second->GetStr(L"bind-ip", ip)) + continue; + ex_astr _ip; + ex_wstr2astr(ip, _ip); + + int port; + it->second->GetInt(L"bind-port", port, 52189); + + jr_data[_name.c_str()]["enable"] = enabled; + jr_data[_name.c_str()]["ip"] = _ip; + jr_data[_name.c_str()]["port"] = port; + } + } + + _create_json_ret(buf, TPE_OK, jr_data); +} + +void TsHttpRpc::_rpc_func_request_session(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#request_session + + int conn_id = 0; + ex_rv rv = TPE_OK; + + if (json_param["conn_id"].isNull()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + if (!json_param["conn_id"].isInt()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + conn_id = json_param["conn_id"].asInt(); + if (0 == conn_id) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + TS_CONNECT_INFO* info = new TS_CONNECT_INFO; + if ((rv = ts_web_rpc_get_conn_info(conn_id, *info)) != TPE_OK) + { + _create_json_ret(buf, rv); + return; + } + +// info->ref_count = 0; +// info->ticket_start = ex_get_tick_count(); +// + // 生成一个session-id(内部会避免重复) + ex_astr sid; + if (!g_session_mgr.request_session(sid, info)) { + _create_json_ret(buf, TPE_FAILED); + return; + } + + EXLOGD("[core] rpc new session-id: %s\n", sid.c_str()); + + Json::Value jr_data; + jr_data["sid"] = sid; + + _create_json_ret(buf, TPE_OK, jr_data); +} + +void TsHttpRpc::_rpc_func_kill_sessions(const Json::Value& json_param, ex_astr& buf) { + /* + { + "sessions": ["0123456", "ABCDEF", ...] + } + */ + + if (json_param.isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + if (json_param["sessions"].isNull() || !json_param["sessions"].isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + Json::Value s = json_param["sessions"]; + int cnt = s.size(); + for (int i = 0; i < cnt; ++i) + { + if (!s[i].isString()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + } + + EXLOGV("[core] kill %d sessions.\n", cnt); + + ex_astr ss = s.toStyledString(); + + g_tpp_mgr.kill_sessions(ss); + + _create_json_ret(buf, TPE_OK); +} + + +void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc + // 加密一个字符串 [ p=plain-text, c=cipher-text ] + // 入参: {"p":"need be encrypt"} + // 示例: {"p":"this-is-a-password"} + // p: 被加密的字符串 + // 返回: + // data域中的"c"的内容是加密后密文的base64编码结果 + // 示例: {"code":0, "data":{"c":"Mxs340a9r3fs+3sdf=="}} + // 错误返回: {"code":1234} + + if (json_param.isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + ex_astr plain_text; + + if (json_param["p"].isNull() || !json_param["p"].isString()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + plain_text = json_param["p"].asCString(); + if (plain_text.length() == 0) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + ex_astr cipher_text; + + if (!ts_db_field_encrypt(plain_text, cipher_text)) + { + _create_json_ret(buf, TPE_FAILED); + return; + } + + Json::Value jr_data; + jr_data["c"] = cipher_text; + _create_json_ret(buf, TPE_OK, jr_data); +} + +void TsHttpRpc::_rpc_func_set_config(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#set_config + /* + { + "noop-timeout": 15 # 按分钟计 + } + */ + + if (json_param.isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + if (json_param["noop_timeout"].isNull() || !json_param["noop_timeout"].isUInt()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + int noop_timeout = json_param["noop_timeout"].asUInt(); + if (noop_timeout == 0) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + //static TppManager g_tpp_mgr; + EXLOGV("[core] no-op timeout set to %d minutes.\n", noop_timeout); + g_tpp_mgr.set_config(noop_timeout * 60); // 内部按秒计,因此要 *60 + + +// Json::Value jr_data; +// jr_data["c"] = cipher_text; +// _create_json_ret(buf, TPE_OK, jr_data); + _create_json_ret(buf, TPE_OK); +} + + +/* +void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf) +{ + // https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc + // 加密多个个字符串 [ p=plain-text, c=cipher-text ] + // 入参: {"p":["need be encrypt", "plain to cipher"]} + // 示例: {"p":["password-for-A"]} + // p: 被加密的字符串 + // 返回: + // data域中的"c"的内容是加密后密文的base64编码结果 + // 示例: {"code":0, "data":{"c":["Mxs340a9r3fs+3sdf=="]}} + // 错误返回: {"code":1234} + + if (json_param.isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + ex_astr plain_text; + + if (json_param["p"].isNull() || !json_param["p"].isArray()) + { + _create_json_ret(buf, TPE_PARAM); + return; + } + + Json::Value c; + + Json::Value p = json_param["p"]; + int cnt = p.size(); + for (int i = 0; i < cnt; ++i) + { + if (!p[i].isString()) { + _create_json_ret(buf, TPE_PARAM); + return; + } + + ex_astr p_txt = p[i].asCString(); + if (p_txt.length() == 0) { + c["c"].append(""); + } + + ex_astr c_txt; + if (!ts_db_field_encrypt(p_txt, c_txt)) + { + _create_json_ret(buf, TPE_FAILED); + return; + } + + c["c"].append(c_txt); + } + + Json::Value jr_data; + jr_data["c"] = c; + _create_json_ret(buf, TPE_OK, jr_data); +} +*/ diff --git a/server/tp_core/core/ts_http_rpc.h b/server/tp_core/core/ts_http_rpc.h index 206c80f..c99c241 100644 --- a/server/tp_core/core/ts_http_rpc.h +++ b/server/tp_core/core/ts_http_rpc.h @@ -37,6 +37,8 @@ private: void _rpc_func_set_config(const Json::Value& json_param, ex_astr& buf); // 请求一个会话ID void _rpc_func_request_session(const Json::Value& json_param, ex_astr& buf); + // 强行终止会话 + void _rpc_func_kill_sessions(const Json::Value& json_param, ex_astr& buf); // 加密一个字符串(返回的是密文的BASE64编码) void _rpc_func_enc(const Json::Value& json_param, ex_astr& buf); // 要求整个核心服务退出 diff --git a/server/tp_core/protocol/ssh/ssh_proxy.cpp b/server/tp_core/protocol/ssh/ssh_proxy.cpp index 6c4e89c..a775d81 100644 --- a/server/tp_core/protocol/ssh/ssh_proxy.cpp +++ b/server/tp_core/protocol/ssh/ssh_proxy.cpp @@ -87,6 +87,20 @@ void SshProxy::set_cfg(TPP_SET_CFG_ARGS* args) { m_noop_timeout_sec = args->noop_timeout; } +void SshProxy::kill_sessions(const ex_astrs& sessions) { + ExThreadSmartLock locker(m_lock); + ts_ssh_sessions::iterator it; + for (it = m_sessions.begin(); it != m_sessions.end(); ++it) { + for (size_t i = 0; i < sessions.size(); ++i) { + if (it->first->sid() == sessions[i]) { + EXLOGW("[ssh] try to kill %s\n", sessions[i].c_str()); + it->first->check_noop_timeout(0, 0); // 立即结束 + } + } + } +} + + void SshProxy::_thread_loop() { EXLOGI("[ssh] TeleportServer-SSH ready on %s:%d\n", m_host_ip.c_str(), m_host_port); diff --git a/server/tp_core/protocol/ssh/ssh_proxy.h b/server/tp_core/protocol/ssh/ssh_proxy.h index 55c3259..452cbfc 100644 --- a/server/tp_core/protocol/ssh/ssh_proxy.h +++ b/server/tp_core/protocol/ssh/ssh_proxy.h @@ -16,6 +16,7 @@ public: bool init(); void timer(); void set_cfg(TPP_SET_CFG_ARGS* args); + void kill_sessions(const ex_astrs& sessions); void session_finished(SshSession* sess); diff --git a/server/tp_core/protocol/ssh/ssh_session.cpp b/server/tp_core/protocol/ssh/ssh_session.cpp index d646a4f..29784b7 100644 --- a/server/tp_core/protocol/ssh/ssh_session.cpp +++ b/server/tp_core/protocol/ssh/ssh_session.cpp @@ -191,11 +191,14 @@ void SshSession::_check_channels() { || (cli == NULL && srv != NULL && ssh_channel_is_closed(srv)) || (srv == NULL && cli != NULL && ssh_channel_is_closed(cli)) ) { - if (cli) + if (cli) { ssh_channel_free(cli); - if (srv) + cli = NULL; + } + if (srv) { ssh_channel_free(srv); - + srv = NULL; + } _record_end((*it)); delete(*it); @@ -407,8 +410,11 @@ void SshSession::check_noop_timeout(ex_u32 t_now, ex_u32 timeout) { for (; it != m_channels.end(); ++it) { if ((*it)->need_close) continue; - if (t_now - (*it)->last_access_timestamp > timeout) { - EXLOGW("[ssh] need close channel by timeout.\n"); + if (t_now == 0) + EXLOGW("[ssh] try close channel by kill.\n"); + else if (t_now - (*it)->last_access_timestamp > timeout) + EXLOGW("[ssh] try close channel by timeout.\n"); + if (t_now == 0 || t_now - (*it)->last_access_timestamp > timeout) { (*it)->need_close = true; m_have_error = true; } diff --git a/server/tp_core/protocol/ssh/ssh_session.h b/server/tp_core/protocol/ssh/ssh_session.h index 2a1e98f..dec1e1f 100644 --- a/server/tp_core/protocol/ssh/ssh_session.h +++ b/server/tp_core/protocol/ssh/ssh_session.h @@ -79,6 +79,8 @@ public: // void check_noop_timeout(ex_u32 t_now, ex_u32 timeout); + const ex_astr& sid() { return m_sid; } + protected: void _thread_loop(void); void _set_stop_flag(void); diff --git a/server/tp_core/protocol/ssh/tpssh.cpp b/server/tp_core/protocol/ssh/tpssh.cpp index fb0ff5d..ceb2712 100644 --- a/server/tp_core/protocol/ssh/tpssh.cpp +++ b/server/tp_core/protocol/ssh/tpssh.cpp @@ -2,6 +2,8 @@ #include "tpp_env.h" #include +#include + TPP_API ex_rv tpp_init(TPP_INIT_ARGS* init_args) { @@ -42,3 +44,45 @@ TPP_API void tpp_timer(void) { TPP_API void tpp_set_cfg(TPP_SET_CFG_ARGS* cfg_args) { g_ssh_proxy.set_cfg(cfg_args); } + +static ex_rv _kill_sessions(const char* param) { + Json::Value jp; + Json::Reader jreader; + + if (!jreader.parse(param, jp)) + return TPE_JSON_FORMAT; + + if (!jp.isArray()) + return TPE_PARAM; + + ex_astrs ss; + int cnt = jp.size(); + for (int i = 0; i < cnt; ++i) + { + if (!jp[i].isString()) { + return TPE_PARAM; + } + + ss.push_back(jp[i].asString()); + } + + g_ssh_proxy.kill_sessions(ss); + + return TPE_PARAM; +} + +TPP_API ex_rv tpp_command(ex_u32 cmd, const char* param) { + switch (cmd) { + case TPP_CMD_KILL_SESSIONS: + if (param == NULL || strlen(param) == 0) + return TPE_PARAM; + return _kill_sessions(param); + default: + return TPE_UNKNOWN_CMD; + } + + return TPE_NOT_IMPLEMENT; +} + + + diff --git a/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj b/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj index 213e064..13f0770 100644 --- a/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj +++ b/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj @@ -68,7 +68,7 @@ Level3 Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;TPP_EXPORTS;LIBSSH_STATIC;%(PreprocessorDefinitions) - ..\..\..\..\common\teleport;..\..\..\..\common\libex\include;..\..\..\..\external\libssh-win-static\include;%(AdditionalIncludeDirectories) + ../../../../common\teleport;../../../../common\libex\include;../../../../external/jsoncpp/include;../../../../external\libssh-win-static\include;%(AdditionalIncludeDirectories) MultiThreadedDebug @@ -86,7 +86,7 @@ true true WIN32;NDEBUG;_WINDOWS;_USRDLL;TPP_EXPORTS;LIBSSH_STATIC;%(PreprocessorDefinitions) - ..\..\..\..\common\teleport;..\..\..\..\common\libex\include;..\..\..\..\external\libssh-win-static\include;%(AdditionalIncludeDirectories) + ../../../../common\teleport;../../../../common\libex\include;../../../../external/jsoncpp/include;../../../../external\libssh-win-static\include;%(AdditionalIncludeDirectories) MultiThreaded @@ -109,6 +109,7 @@ + @@ -133,6 +134,9 @@ + + + diff --git a/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj.filters b/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj.filters index 27e1dcc..a4fd071 100644 --- a/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj.filters +++ b/server/tp_core/protocol/ssh/tpssh.vs2015.vcxproj.filters @@ -1,164 +1,179 @@ -锘 - - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {73f964a0-2430-47c6-b083-956809018ec2} - - - {68aa1482-4f0d-43b5-9bbd-d7857e9b540b} - - - {e6941d07-304a-4ba8-af5a-c5a09dae61b4} - - - {4a7ed97e-93c1-4513-b813-3399eaeb8a2f} - - - {b95409eb-4905-440f-8537-ee892e96b49a} - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {f1fea9ae-123c-4aa8-a152-e88d1d0a29fd} - - - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - libex\header - - - win32 - - - win32 - - - main app - - - main app - - - common - - - common - - - common - - - common - - - main app - - - common - - - main app - - - libssh - - - libssh - - - libssh - - - libssh - - - - - main app - - - libex\src - - - libex\src - - - libex\src - - - libex\src - - - libex\src - - - libex\src - - - libex\src - - - win32 - - - win32 - - - main app - - - main app - - - common - - - common - - - main app - - - common - - - main app - - +锘 + + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {73f964a0-2430-47c6-b083-956809018ec2} + + + {68aa1482-4f0d-43b5-9bbd-d7857e9b540b} + + + {e6941d07-304a-4ba8-af5a-c5a09dae61b4} + + + {4a7ed97e-93c1-4513-b813-3399eaeb8a2f} + + + {b95409eb-4905-440f-8537-ee892e96b49a} + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {f1fea9ae-123c-4aa8-a152-e88d1d0a29fd} + + + {022b0a3d-47c2-40d8-96d9-dceea02e7eef} + + + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + libex\header + + + win32 + + + win32 + + + main app + + + main app + + + common + + + common + + + common + + + common + + + main app + + + common + + + main app + + + libssh + + + libssh + + + libssh + + + libssh + + + jsoncpp + + + + + main app + + + libex\src + + + libex\src + + + libex\src + + + libex\src + + + libex\src + + + libex\src + + + libex\src + + + win32 + + + win32 + + + main app + + + main app + + + common + + + common + + + main app + + + common + + + main app + + + jsoncpp + + + jsoncpp + + + jsoncpp + + \ No newline at end of file diff --git a/server/www/teleport/static/js/audit/record-list.js b/server/www/teleport/static/js/audit/record-list.js index 5cd36f9..b8bea97 100644 --- a/server/www/teleport/static/js/audit/record-list.js +++ b/server/www/teleport/static/js/audit/record-list.js @@ -80,7 +80,7 @@ $app.create_controls = function (cb_stack) { fields: {time_begin: 'time_begin'} }, { - title: '鑰楁椂', + title: '鏃堕暱', key: 'time_cost', render: 'time_cost', fields: {time_begin: 'time_begin', time_end: 'time_end', state: 'state'} diff --git a/server/www/teleport/static/js/ops/session-list.js b/server/www/teleport/static/js/ops/session-list.js index 1be989f..8c899d3 100644 --- a/server/www/teleport/static/js/ops/session-list.js +++ b/server/www/teleport/static/js/ops/session-list.js @@ -45,6 +45,13 @@ $app.create_controls = function (cb_stack) { sort_asc: false, fields: {id: 'id'} }, + { + title: '浼氳瘽ID', + key: 'sid', + sort: true, + sort_asc: false, + fields: {sid: 'sid'} + }, { title: '鐢ㄦ埛', key: 'user', @@ -90,7 +97,7 @@ $app.create_controls = function (cb_stack) { fields: {time_begin: 'time_begin'} }, { - title: '鑰楁椂', + title: '鏃堕暱', key: 'time_cost', render: 'time_cost', fields: {time_begin: 'time_begin', time_end: 'time_end', state: 'state'} @@ -170,30 +177,22 @@ $app.create_controls = function (cb_stack) { $app.on_table_session_cell_created = function (tbl, row_id, col_key, cell_obj) { if (col_key === 'chkbox') { cell_obj.find('[data-check-box]').click(function () { + // 鍚屾鐩稿悓浼氳瘽ID鐨勯変腑鐘舵 + var _obj = $(this); + var checked = _obj.is(':checked'); + var _row_data = tbl.get_row(_obj); + var _objs = $('#' + $app.table_session.dom_id + ' tbody').find('[data-check-box]'); + $.each(_objs, function (i, _o) { + var _rd = tbl.get_row(_o); + if (_row_data.sid === _rd.sid) { + $(_o).prop('checked', checked); + } + }); + + $app.check_host_all_selected(); }); } - // else if (col_key === 'action') { - // // 缁戝畾绯荤粺閫夋嫨妗嗕簨浠 - // cell_obj.find('[data-action]').click(function () { - // var action = $(this).attr('data-action'); - // if (action === 'edit') { - // $app.dlg_edit_host.show_edit(row_id); - // } else if (action === 'account') { - // $app.dlg_accounts.show(row_id); - // } - // }); - // } else if (col_key === 'ip') { - // cell_obj.find('[data-toggle="popover"]').popover({trigger: 'hover'}); - // // } else if (col_key === 'account') { - // // cell_obj.find('[data-action="add-account"]').click(function () { - // // $app.dlg_accounts.show(row_id); - // // }); - // } else if (col_key === 'account_count') { - // cell_obj.find('[data-action="edit-account"]').click(function () { - // $app.dlg_accounts.show(row_id); - // }); - // } }; $app.check_host_all_selected = function (cb_stack) { @@ -389,18 +388,48 @@ $app.on_table_session_header_created = function (header) { //header._table_ctrl.get_filter_ctrl('host_state').on_created(); }; -$app.get_selected_session = function (tbl) { +$app.get_selected_sessions = function (tbl) { var records = []; var _objs = $('#' + $app.table_session.dom_id + ' tbody tr td input[data-check-box]'); $.each(_objs, function (i, _obj) { if ($(_obj).is(':checked')) { var _row_data = tbl.get_row(_obj); - records.push(_row_data.id); + records.push(_row_data.sid); } }); return records; }; $app.on_btn_kill_sessions_click = function () { - $tp.notify_error('鎶辨瓑锛屾鍔熻兘灏氭湭瀹炵幇锛'); + var sessions = $app.get_selected_sessions($app.table_session); + console.log(sessions); + if (sessions.length === 0) { + $tp.notify_error('璇烽夋嫨瑕佸己琛岀粓姝㈢殑浼氳瘽锛'); + return; + } + + var _fn_sure = function (cb_stack, cb_args) { + $tp.ajax_post_json('/ops/kill', {sessions: sessions}, + function (ret) { + if (ret.code === TPE_OK) { + $app.table_session.load_data(); + $tp.notify_success('寮鸿缁堟浼氳瘽鎿嶄綔鎴愬姛锛'); + } else { + $tp.notify_error('寮鸿缁堟浼氳瘽澶辫触锛' + tp_error_msg(ret.code, ret.message)); + } + + cb_stack.exec(); + }, + function () { + $tp.notify_error('缃戠粶鏁呴殰锛屽己琛岀粓姝細璇濇搷浣滃け璐ワ紒'); + cb_stack.exec(); + } + ); + }; + + var cb_stack = CALLBACK_STACK.create(); + $tp.dlg_confirm(cb_stack, { + msg: '鎮ㄧ‘瀹氳寮鸿缁堟杩欎簺浼氳瘽鍚楋紵', + fn_yes: _fn_sure + }); }; diff --git a/server/www/teleport/view/ops/sessions.mako b/server/www/teleport/view/ops/sessions.mako index b3a85b8..2742631 100644 --- a/server/www/teleport/view/ops/sessions.mako +++ b/server/www/teleport/view/ops/sessions.mako @@ -34,12 +34,12 @@
-##
-##
-##
-## -##
-##
+
+
+
+ +
+
    @@ -59,4 +59,10 @@
    +
    +

    璇存槑锛

    +
      +
    • 娉ㄦ剰锛氬己鍒朵腑鏂細璇濇椂锛岀浉鍚屼細璇滻D鐨勪細璇濓紙渚嬪浣跨敤SecureCRT鎴栬厁Shell瀹㈡埛绔殑鈥滃厠闅嗕細璇濃濆姛鑳芥墦寮鐨勪細璇濓級鍧囦細琚腑鏂
    • +
    +
    diff --git a/server/www/teleport/webroot/app/controller/__init__.py b/server/www/teleport/webroot/app/controller/__init__.py index 2396a00..6f1c56f 100644 --- a/server/www/teleport/webroot/app/controller/__init__.py +++ b/server/www/teleport/webroot/app/controller/__init__.py @@ -162,6 +162,8 @@ controllers = [ (r'/ops/get-remotes', ops.DoGetRemotesHandler), # - [json] 鏋勫缓鎺堟潈鏄犲皠琛 (r'/ops/build-auz-map', ops.DoBuildAuzMapHandler), + # - [json] 寮鸿缁堟鎸囧畾浼氳瘽 + (r'/ops/kill', ops.DoKillSessionsHandler), # ==================================================== # 瀹¤鐩稿叧 diff --git a/server/www/teleport/webroot/app/controller/ops.py b/server/www/teleport/webroot/app/controller/ops.py index 6677929..f16dc47 100644 --- a/server/www/teleport/webroot/app/controller/ops.py +++ b/server/www/teleport/webroot/app/controller/ops.py @@ -736,6 +736,32 @@ class DoGetRemotesHandler(TPBaseJsonHandler): self.write_json(err, data=ret) +class DoKillSessionsHandler(TPBaseJsonHandler): + @tornado.gen.coroutine + def post(self): + ret = self.check_privilege(TP_PRIVILEGE_OPS_AUZ) + if ret != TPE_OK: + return + + args = self.get_argument('args', None) + if args is None: + return self.write_json(TPE_PARAM) + try: + args = json.loads(args) + except: + return self.write_json(TPE_JSON_FORMAT) + + try: + sessions = args['sessions'] + except: + return self.write_json(TPE_PARAM) + + req = {'method': 'kill_sessions', 'param': {'sessions': sessions}} + _yr = core_service_async_post_http(req) + _err, _ = yield _yr + self.write_json(_err) + + class DoBuildAuzMapHandler(TPBaseJsonHandler): def post(self): ret = self.check_privilege(TP_PRIVILEGE_OPS_AUZ) diff --git a/server/www/teleport/webroot/app/model/record.py b/server/www/teleport/webroot/app/model/record.py index 3ed2355..2fdeb52 100644 --- a/server/www/teleport/webroot/app/model/record.py +++ b/server/www/teleport/webroot/app/model/record.py @@ -100,8 +100,8 @@ def get_records(handler, sql_filter, sql_order, sql_limit, sql_restrict, sql_exc s.order_by('r.id', _sort) elif 'time_begin' == sql_order['name']: s.order_by('r.time_begin', _sort) - # elif 'os_type' == sql_order['name']: - # s.order_by('h.os_type', _sort) + elif 'sid' == sql_order['name']: + s.order_by('r.sid', _sort) # elif 'cid' == sql_order['name']: # s.order_by('h.cid', _sort) # elif 'state' == sql_order['name']: