改进:

1. SSH按通道进行记录录像,避免一个会话多个通道时录像数据混杂导致播放混乱;
  2. 无论是SSH-Shell还是SSH-SFTP,均能够根据实际建立连接并进行操作正确记录其类型,方便界面展示;
  3. 核心模块能够处理连接信息的引用计数,因此SFTP上编辑、保存文件可以正常工作了;
  4. 能够正确处理绝大部分断开连接后状态还在“使用中”的问题,但SecureCRT通过“连接SFTP标签页”后,有时仍然存在此问题。
pull/105/head
Apex Liu 2017-11-20 04:19:04 +08:00
parent 83b9ba01bc
commit 907cb1b9e1
19 changed files with 452 additions and 555 deletions

View File

@ -40,7 +40,7 @@
//=======================================================
// 远程连接会话状态
//=======================================================
#define TP_SESS_STAT_RUNNING 0 // 会话开始了,尚未结束
#define TP_SESS_STAT_RUNNING 0 // 会话开始了,正在连接
#define TP_SESS_STAT_END 9999 // 会话成功结束
#define TP_SESS_STAT_ERR_AUTH_DENIED 1 // 会话结束,因为认证失败
#define TP_SESS_STAT_ERR_CONNECT 2 // 会话结束,因为无法连接到远程主机

View File

@ -1,8 +1,5 @@
#include <memory>
//#include <sys/types.h>
//#include <sys/stat.h>
#include "base_record.h"
TppRecBase::TppRecBase()

View File

@ -85,8 +85,6 @@ public:
bool begin(const wchar_t* base_path, const wchar_t* base_fname, int record_id, const TPP_CONNECT_INFO* info);
bool end();
//virtual void record(ex_u8 type, const ex_u8* data, size_t size) = 0;
protected:
virtual bool _on_begin(const TPP_CONNECT_INFO* info) = 0;
virtual bool _on_end() = 0;

View File

@ -44,7 +44,7 @@ typedef struct TPP_CONNECT_INFO
typedef TPP_CONNECT_INFO* (*TPP_GET_CONNNECT_INFO_FUNC)(const char* sid);
typedef void(*TPP_FREE_CONNECT_INFO_FUNC)(TPP_CONNECT_INFO* info);
typedef bool(*TPP_SESSION_BEGIN_FUNC)(const TPP_CONNECT_INFO* info, int* db_id);
typedef bool(*TPP_SESSION_UPDATE_FUNC)(int db_id, int state);
typedef bool(*TPP_SESSION_UPDATE_FUNC)(int db_id, int protocol_sub_type, int state);
typedef bool(*TPP_SESSION_END_FUNC)(const char* sid, int db_id, int ret);

View File

@ -359,9 +359,9 @@ void TsHttpRpc::_rpc_func_request_session(const Json::Value& json_param, ex_astr
return;
}
info->ref_count = 0;
info->ticket_start = ex_get_tick_count();
// info->ref_count = 0;
// info->ticket_start = ex_get_tick_count();
//
// 生成一个session-id内部会避免重复
ex_astr sid;
if (!g_session_mgr.request_session(sid, info)) {

View File

@ -54,6 +54,8 @@ void tpp_free_connect_info(TPP_CONNECT_INFO* info)
if (NULL == info)
return;
g_session_mgr.free_connect_info(info->sid);
free(info->sid);
free(info->user_username);
free(info->host_ip);
@ -90,8 +92,8 @@ bool tpp_session_begin(const TPP_CONNECT_INFO* info, int* db_id)
return ts_web_rpc_session_begin(sinfo, *db_id);
}
bool tpp_session_update(int db_id, int state) {
return ts_web_rpc_session_update(db_id, state);
bool tpp_session_update(int db_id, int protocol_sub_type, int state) {
return ts_web_rpc_session_update(db_id, protocol_sub_type, state);
}
bool tpp_session_end(const char* sid, int db_id, int ret)

View File

@ -40,7 +40,7 @@ void TsSessionManager::_set_stop_flag(void)
void TsSessionManager::_remove_expired_connect_info(void)
{
// 超过30秒未进行连接的connect-info会被移除
// 超过15秒未进行连接的connect-info会被移除
ExThreadSmartLock locker(m_lock);
@ -48,15 +48,13 @@ void TsSessionManager::_remove_expired_connect_info(void)
ts_connections::iterator it = m_connections.begin();
for (; it != m_connections.end(); )
{
#ifdef EX_DEBUG
if (it->second->ref_count == 0 && _now - it->second->ticket_start >= 60*1000*60)
#else
if (it->second->ref_count == 0 && _now - it->second->ticket_start >= 30000)
#endif
//EXLOGD("[core] check expired connect info: [%s] %d, %d %d %d\n", it->first.c_str(), it->second->ref_count, int(_now), int(it->second->ticket_start), int(_now - it->second->ticket_start));
if (it->second->ref_count == 0 && _now - it->second->ticket_start > 15000)
{
EXLOGV("[core] remove connection info: %s\n", it->first.c_str());
EXLOGD("[core] remove connection info, because timeout: %s\n", it->first.c_str());
delete it->second;
m_connections.erase(it++);
EXLOGD("[core] there are %d connection info exists.\n", m_connections.size());
}
else
{
@ -96,6 +94,24 @@ bool TsSessionManager::get_connect_info(const ex_astr& sid, TS_CONNECT_INFO& inf
return true;
}
bool TsSessionManager::free_connect_info(const ex_astr& sid) {
ExThreadSmartLock locker(m_lock);
ts_connections::iterator it = m_connections.find(sid);
if (it == m_connections.end())
return false;
it->second->ref_count--;
if (it->second->ref_count <= 0) {
EXLOGD("[core] remove connection info, because all connections closed: %s\n", it->first.c_str());
delete it->second;
m_connections.erase(it);
EXLOGD("[core] there are %d connection info exists.\n", m_connections.size());
}
return true;
}
bool TsSessionManager::request_session(ex_astr& sid, TS_CONNECT_INFO* info)
{
ExThreadSmartLock locker(m_lock);
@ -118,6 +134,8 @@ bool TsSessionManager::request_session(ex_astr& sid, TS_CONNECT_INFO* info)
}
info->sid = _sid;
info->ref_count = 0;
info->ticket_start = ex_get_tick_count();
m_connections.insert(std::make_pair(_sid, info));
sid = _sid;

View File

@ -50,20 +50,10 @@ public:
// generate a sid for connection info.
bool request_session(ex_astr& sid, TS_CONNECT_INFO* info);
// +ref for connection-info.
bool session_connect(const ex_astr& sid, TPP_CONNECT_INFO** info);
// free connection-info created by session_connect().
bool free_connect_info(TPP_CONNECT_INFO* info);
// -ref for connecton-info, and release it when ref is 0.
bool session_end(const ex_astr& sid);
// TODO:
void timer();
// 根据sid得到session信息
// 根据sid得到连接信息并增加引用计数
bool get_connect_info(const ex_astr& sid, TS_CONNECT_INFO& info);
// 减少引用计数当引用计数为0时删除之
bool free_connect_info(const ex_astr& sid);
protected:
// Ïß³ÌÑ­»·

View File

@ -263,11 +263,12 @@ bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id)
return true;
}
bool ts_web_rpc_session_update(int record_id, int state) {
bool ts_web_rpc_session_update(int record_id, int protocol_sub_type, int state) {
Json::FastWriter json_writer;
Json::Value jreq;
jreq["method"] = "session_update";
jreq["param"]["rid"] = record_id;
jreq["param"]["protocol_sub_type"] = protocol_sub_type;
jreq["param"]["code"] = state;
ex_astr json_param;

View File

@ -14,7 +14,7 @@ int ts_web_rpc_get_conn_info(int conn_id, TS_CONNECT_INFO& info);
// 记录会话的开始
bool ts_web_rpc_session_begin(TS_CONNECT_INFO& info, int& record_id);
// update session state
bool ts_web_rpc_session_update(int id, int state);
bool ts_web_rpc_session_update(int id, int protocol_sub_type, int state);
//session 结束
bool ts_web_rpc_session_end(const char* sid, int id, int ret_code);

View File

@ -16,13 +16,6 @@ SshProxy::~SshProxy()
ssh_bind_free(m_bind);
ssh_finalize();
ts_sftp_sessions::iterator it;
for (it = m_sftp_sessions.begin(); it != m_sftp_sessions.end(); ++it)
{
delete it->second;
}
m_sftp_sessions.clear();
}
bool SshProxy::init()
@ -70,7 +63,7 @@ bool SshProxy::init()
}
void SshProxy::timer() {
// be called per one second.
// timer() will be called per one second, and I will do my job per 5 seconds.
m_timer_counter++;
if(m_timer_counter < 5)
return;
@ -88,36 +81,7 @@ void SshProxy::timer() {
void SshProxy::_thread_loop()
{
EXLOGV("[ssh] TeleportServer-SSH ready on %s:%d\n", m_host_ip.c_str(), m_host_port);
_run();
EXLOGV("[ssh] main-loop end.\n");
}
void SshProxy::_set_stop_flag()
{
m_stop_flag = true;
if (m_is_running)
{
// 用一个变通的方式来结束阻塞中的监听,就是连接一下它。
ex_astr host_ip = m_host_ip;
if (host_ip == "0.0.0.0")
host_ip = "127.0.0.1";
ssh_session _session = ssh_new();
ssh_options_set(_session, SSH_OPTIONS_HOST, host_ip.c_str());
ssh_options_set(_session, SSH_OPTIONS_PORT, &m_host_port);
int _timeout_us = 10;
ssh_options_set(_session, SSH_OPTIONS_TIMEOUT, &_timeout_us);
ssh_connect(_session);
ssh_free(_session);
}
m_thread_mgr.stop_all();
}
void SshProxy::_run()
{
for (;;)
{
// 注意ssh_new()出来的指针如果遇到停止标志本函数内部就释放了否则这个指针交给了SshSession类实例管理其析构时会释放。
@ -169,92 +133,38 @@ void SshProxy::_run()
// 等待所有工作线程退出
m_thread_mgr.stop_all();
EXLOGV("[ssh] main-loop end.\n");
}
void SshProxy::_dump_sftp_sessions()
void SshProxy::_set_stop_flag()
{
ts_sftp_sessions::iterator it = m_sftp_sessions.begin();
for (; it != m_sftp_sessions.end(); ++it)
{
EXLOGD("[ssh] ssh-proxy session: sid: %s\n", it->first.c_str());
}
}
m_stop_flag = true;
void SshProxy::add_sftp_session_info(const ex_astr& sid, const ex_astr& host_ip, int host_port, const ex_astr& user_name, const ex_astr& user_auth, int auth_mode)
{
ExThreadSmartLock locker(m_lock);
EXLOGD("[ssh] add sftp session-id: %s\n", sid.c_str());
ts_sftp_sessions::iterator it = m_sftp_sessions.find(sid);
if (it != m_sftp_sessions.end())
if (m_is_running)
{
EXLOGD("[ssh] sftp-session-id '%s' already exists.\n", sid.c_str());
it->second->ref_count++;
return;
// 用一个变通的方式来结束阻塞中的监听,就是连接一下它。
ex_astr host_ip = m_host_ip;
if (host_ip == "0.0.0.0")
host_ip = "127.0.0.1";
ssh_session _session = ssh_new();
ssh_options_set(_session, SSH_OPTIONS_HOST, host_ip.c_str());
ssh_options_set(_session, SSH_OPTIONS_PORT, &m_host_port);
int _timeout_us = 10;
ssh_options_set(_session, SSH_OPTIONS_TIMEOUT, &_timeout_us);
ssh_connect(_session);
ssh_free(_session);
}
TS_SFTP_SESSION_INFO* info = new TS_SFTP_SESSION_INFO;
info->host_ip = host_ip;
info->host_port = host_port;
info->user_name = user_name;
info->user_auth = user_auth;
info->auth_mode = auth_mode;
info->ref_count = 1;
if (!m_sftp_sessions.insert(std::make_pair(sid, info)).second)
{
EXLOGE("[ssh] ssh-proxy can not insert a sftp-session-id.\n");
}
_dump_sftp_sessions();
}
bool SshProxy::get_sftp_session_info(const ex_astr& sid, TS_SFTP_SESSION_INFO& info)
{
ExThreadSmartLock locker(m_lock);
EXLOGD("[ssh] try to get info by sftp session-id: %s\n", sid.c_str());
_dump_sftp_sessions();
ts_sftp_sessions::iterator it = m_sftp_sessions.find(sid);
if (it == m_sftp_sessions.end())
{
EXLOGD("[ssh] sftp-session '%s' not exists.\n", sid.c_str());
return false;
}
info.host_ip = it->second->host_ip;
info.host_port = it->second->host_port;
info.user_name = it->second->user_name;
info.user_auth = it->second->user_auth;
info.auth_mode = it->second->auth_mode;
info.ref_count = it->second->ref_count;
return true;
}
void SshProxy::remove_sftp_sid(const ex_astr& sid)
{
EXLOGD("[ssh] try to remove sftp session-id: %s\n", sid.c_str());
ExThreadSmartLock locker(m_lock);
ts_sftp_sessions::iterator it = m_sftp_sessions.find(sid);
if (it == m_sftp_sessions.end())
{
EXLOGE("[ssh] ssh-proxy when remove sftp sid, it not in charge.\n");
return;
}
it->second->ref_count--;
if (it->second->ref_count <= 0)
{
delete it->second;
m_sftp_sessions.erase(it);
EXLOGD("[ssh] sftp session-id '%s' removed.\n", sid.c_str());
}
m_thread_mgr.stop_all();
}
void SshProxy::session_finished(SshSession* sess)
{
// TODO: 向核心模块汇报此会话终止,以减少对应连接信息的引用计数
ExThreadSmartLock locker(m_lock);
ts_ssh_sessions::iterator it = m_sessions.find(sess);
if (it != m_sessions.end())

View File

@ -7,18 +7,6 @@
typedef std::map<SshSession*, unsigned char> ts_ssh_sessions;
typedef struct TS_SFTP_SESSION_INFO
{
ex_astr host_ip;
int host_port;
ex_astr user_name;
ex_astr user_auth;
int auth_mode;
int ref_count; // 引用计数器但所有引用本登录信息的ssh-sftp通道关闭就销毁之
}TS_SFTP_SESSION_INFO;
typedef std::map<ex_astr, TS_SFTP_SESSION_INFO*> ts_sftp_sessions;
class SshProxy : public ExThreadBase
{
public:
@ -28,31 +16,14 @@ public:
bool init();
void timer();
void add_sftp_session_info(
const ex_astr& sid,
const ex_astr& host_ip,
int host_port,
const ex_astr& user_name,
const ex_astr& user_auth,
int auth_mode
);
bool get_sftp_session_info(const ex_astr& sid, TS_SFTP_SESSION_INFO& info);
void remove_sftp_sid(const ex_astr& sid);
void session_finished(SshSession* sess);
protected:
void _thread_loop();
void _set_stop_flag();
void _run();
private:
void _dump_sftp_sessions();
private:
ssh_bind m_bind;
//bool m_stop_flag;
int m_timer_counter;
ExThreadLock m_lock;
@ -61,7 +32,6 @@ private:
int m_host_port;
ts_ssh_sessions m_sessions;
ts_sftp_sessions m_sftp_sessions;
ExThreadManager m_thread_mgr;
};

View File

@ -5,19 +5,27 @@
#include <algorithm>
#include <teleport_const.h>
TP_SSH_CHANNEL_PAIR::TP_SSH_CHANNEL_PAIR() {
type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
cli_channel = NULL;
srv_channel = NULL;
retcode = TP_SESS_STAT_RUNNING;
db_id = 0;
}
SshSession::SshSession(SshProxy *proxy, ssh_session sess_client) :
ExThreadBase("ssh-session-thread"),
m_proxy(proxy),
m_cli_session(sess_client),
m_srv_session(NULL)
m_srv_session(NULL),
m_conn_info(NULL)
{
m_retcode = TP_SESS_STAT_RUNNING;
m_db_id = 0;
m_auth_type = TP_AUTH_TYPE_PASSWORD;
m_is_first_server_data = true;
m_is_sftp = false;
m_is_logon = false;
m_have_error = false;
@ -44,8 +52,8 @@ SshSession::~SshSession() {
_set_stop_flag();
if (m_is_sftp) {
m_proxy->remove_sftp_sid(m_sid);
if (NULL != m_conn_info) {
g_ssh_env.free_connect_info(m_conn_info);
}
EXLOGD("[ssh] session destroy.\n");
@ -53,7 +61,6 @@ SshSession::~SshSession() {
void SshSession::_thread_loop(void) {
_run();
_on_session_end();
m_proxy->session_finished(this);
}
@ -72,71 +79,85 @@ void SshSession::_set_stop_flag(void) {
}
}
bool SshSession::_on_session_begin(const TPP_CONNECT_INFO* info)
{
if (!g_ssh_env.session_begin(info, &m_db_id))
void SshSession::_session_error(int err_code) {
int db_id = 0;
if (!g_ssh_env.session_begin(m_conn_info, &db_id))
{
EXLOGD("[ssh] session_begin error. %d\n", m_db_id);
EXLOGE("[ssh] can not write session error to database.\n");
return;
}
g_ssh_env.session_end(m_sid.c_str(), db_id, err_code);
}
bool SshSession::_on_session_begin(TP_SSH_CHANNEL_PAIR* cp)
{
if (!g_ssh_env.session_begin(m_conn_info, &(cp->db_id)))
{
EXLOGD("[ssh] session_begin error. %d\n", cp->db_id);
return false;
}
m_rec.begin(g_ssh_env.replay_path.c_str(), L"tp-ssh", m_db_id, info);
if (!g_ssh_env.session_update(cp->db_id, m_conn_info->protocol_sub_type, TP_SESS_STAT_STARTED))
{
EXLOGD("[ssh] session_update error. %d\n", cp->db_id);
return false;
}
cp->rec.begin(g_ssh_env.replay_path.c_str(), L"tp-ssh", cp->db_id, m_conn_info);
return true;
}
bool SshSession::_on_session_end(void)
void SshSession::_on_session_end(TP_SSH_CHANNEL_PAIR* cp)
{
if (m_db_id > 0)
if (cp->db_id > 0)
{
EXLOGD("[ssh] session ret-code: %d\n", m_retcode);
EXLOGD("[ssh] session ret-code: %d\n", cp->retcode);
// 如果会话过程中没有发生错误,则将其状态改为结束,否则记录下错误值
if (m_retcode == TP_SESS_STAT_RUNNING)
m_retcode = TP_SESS_STAT_END;
if (cp->retcode == TP_SESS_STAT_RUNNING || cp->retcode == TP_SESS_STAT_STARTED)
cp->retcode = TP_SESS_STAT_END;
g_ssh_env.session_end(m_sid.c_str(), m_db_id, m_retcode);
g_ssh_env.session_end(m_sid.c_str(), cp->db_id, cp->retcode);
cp->db_id = 0;
}
return true;
}
void SshSession::_close_channels(void) {
ExThreadSmartLock locker(m_lock);
if (m_channel_cli_srv.size() > 0)
EXLOGW("[ssh] when close all channels, %d client channel need close.\n", m_channel_cli_srv.size());
if (m_channel_srv_cli.size() > 0)
EXLOGW("[ssh] when close all channels, %d server channel need close.\n", m_channel_srv_cli.size());
ts_ssh_channel_map::iterator it = m_channel_cli_srv.begin();
for (; it != m_channel_cli_srv.end(); ++it) {
if (!ssh_channel_is_eof(it->first))
ssh_channel_send_eof(it->first);
if (!ssh_channel_is_closed(it->first))
ssh_channel_close(it->first);
ssh_channel_free(it->first);
if (NULL != it->second) {
if (!ssh_channel_is_eof(it->second->channel))
ssh_channel_send_eof(it->second->channel);
if (!ssh_channel_is_closed(it->second->channel))
ssh_channel_close(it->second->channel);
ssh_channel_free(it->second->channel);
delete it->second;
tp_channels::iterator it = m_channels.begin();
for (; it != m_channels.end(); ++it) {
ssh_channel ch = (*it)->srv_channel;
if (ch != NULL) {
if (!ssh_channel_is_closed(ch)) {
if (!ssh_channel_is_eof(ch))
ssh_channel_send_eof(ch);
ssh_channel_close(ch);
}
ssh_channel_free(ch);
}
ch = (*it)->cli_channel;
if (ch != NULL) {
if (!ssh_channel_is_closed(ch)) {
if (!ssh_channel_is_eof(ch))
ssh_channel_send_eof(ch);
ssh_channel_close(ch);
}
ssh_channel_free(ch);
}
_on_session_end(*it);
delete (*it);
}
it = m_channel_srv_cli.begin();
for (; it != m_channel_srv_cli.end(); ++it) {
if (NULL != it->second) {
delete it->second;
}
}
m_channel_cli_srv.clear();
m_channel_srv_cli.clear();
m_channels.clear();
}
void SshSession::_run(void) {
@ -175,7 +196,7 @@ void SshSession::_run(void) {
// 认证,并打开一个通道
int r = 0;
while (!(m_is_logon && m_channel_cli_srv.size() > 0)) {
while (!(m_is_logon && m_channels.size() > 0)) {
if (m_have_error)
break;
r = ssh_event_dopoll(event_loop, -1);
@ -211,7 +232,7 @@ void SshSession::_run(void) {
_close_channels();
}
} while (m_channel_cli_srv.size() > 0);
} while (m_channels.size() > 0);
EXLOGV("[ssh] [%s:%d] all channel in this session are closed.\n", m_client_ip.c_str(), m_client_port);
@ -221,7 +242,12 @@ void SshSession::_run(void) {
}
void SshSession::save_record() {
m_rec.save_record();
ExThreadSmartLock locker(m_lock);
tp_channels::iterator it = m_channels.begin();
for (; it != m_channels.end(); ++it) {
(*it)->rec.save_record();
}
}
@ -232,57 +258,31 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
EXLOGV("[ssh] authenticating, session-id: %s\n", _this->m_sid.c_str());
int protocol = 0;
TPP_CONNECT_INFO* sess_info = g_ssh_env.get_connect_info(_this->m_sid.c_str());
//TPP_CONNECT_INFO* sess_info = g_ssh_env.get_connect_info(_this->m_sid.c_str());
_this->m_conn_info = g_ssh_env.get_connect_info(_this->m_sid.c_str());
if (NULL == sess_info) {
// EXLOGW("[ssh] try to get login-info from ssh-sftp-session.\n");
// 尝试从sftp连接记录中获取连接信息一个ssh会话如果成为sftp会话内部会将连接信息记录下来备用
// TS_SFTP_SESSION_INFO sftp_info;
// if (!_this->m_proxy->get_sftp_session_info(_this->m_sid, sftp_info)) {
EXLOGE("[ssh] no such session: %s\n", _this->m_sid.c_str());
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_AUTH_DENIED;
return SSH_AUTH_DENIED;
// }
//
// _this->m_conn_ip = sftp_info.host_ip;
// _this->m_conn_port = sftp_info.host_port;
// _this->m_auth_type = sftp_info.auth_mode;
// _this->m_acc_name = sftp_info.user_name;
// _this->m_acc_secret = sftp_info.user_auth;
// protocol = TP_PROTOCOL_TYPE_SSH;
//
// // 因为是从sftp会话得来的登录数据因此限制本会话只能用于sftp不允许再使用shell了。
// _this->_enter_sftp_mode();
if (NULL == _this->m_conn_info) {
EXLOGE("[ssh] no such session: %s\n", _this->m_sid.c_str());
_this->m_have_error = true;
_this->_session_error(TP_SESS_STAT_ERR_SESSION);
return SSH_AUTH_DENIED;
}
else {
_this->m_conn_ip = sess_info->conn_ip;
_this->m_conn_port = sess_info->conn_port;
_this->m_auth_type = sess_info->auth_type;
_this->m_acc_name = sess_info->acc_username;
_this->m_acc_secret = sess_info->acc_secret;
protocol = sess_info->protocol_type;
_this->m_conn_ip = _this->m_conn_info->conn_ip;
_this->m_conn_port = _this->m_conn_info->conn_port;
_this->m_auth_type = _this->m_conn_info->auth_type;
_this->m_acc_name = _this->m_conn_info->acc_username;
_this->m_acc_secret = _this->m_conn_info->acc_secret;
protocol = _this->m_conn_info->protocol_type;
}
if (protocol != TP_PROTOCOL_TYPE_SSH) {
g_ssh_env.free_connect_info(sess_info);
EXLOGE("[ssh] session '%s' is not for SSH.\n", _this->m_sid.c_str());
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_AUTH_DENIED;
_this->_session_error(TP_SESS_STAT_ERR_INTERNAL);
return SSH_AUTH_DENIED;
}
if (!_this->_on_session_begin(sess_info))
{
g_ssh_env.free_connect_info(sess_info);
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_AUTH_DENIED;
return SSH_AUTH_DENIED;
}
g_ssh_env.free_connect_info(sess_info);
sess_info = NULL;
// 现在尝试根据session-id获取得到的信息连接并登录真正的SSH服务器
EXLOGV("[ssh] try to connect to real SSH server %s:%d\n", _this->m_conn_ip.c_str(), _this->m_conn_port);
_this->m_srv_session = ssh_new();
@ -306,30 +306,24 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
if (rc != SSH_OK) {
EXLOGE("[ssh] can not connect to real SSH server %s:%d. [%d]%s\n", _this->m_conn_ip.c_str(), _this->m_conn_port, rc, ssh_get_error(_this->m_srv_session));
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_CONNECT;
_this->_session_error(TP_SESS_STAT_ERR_CONNECT);
return SSH_AUTH_ERROR;
}
if (!g_ssh_env.session_update(_this->m_db_id, TP_SESS_STAT_STARTED))
{
EXLOGD("[ssh] session_update error. %d\n", _this->m_db_id);
return false;
}
// // 检查服务端支持的认证协议
// // 检查服务端支持的认证协议
ssh_userauth_none(_this->m_srv_session, NULL);
// rc = ssh_userauth_none(_this->m_srv_session, NULL);
// if (rc == SSH_AUTH_ERROR) {
// EXLOGE("[ssh] invalid password for password mode to login to real SSH server %s:%d.\n", _this->m_server_ip.c_str(), _this->m_server_port);
// _this->m_have_error = true;
// _this->m_retcode = SESS_STAT_ERR_AUTH_DENIED;
// return SSH_AUTH_ERROR;
// }
// // int auth_methods = ssh_userauth_list(_this->m_srv_session, NULL);
// const char* banner = ssh_get_issue_banner(_this->m_srv_session);
// if (NULL != banner) {
// EXLOGE("[ssh] issue banner: %s\n", banner);
// }
// rc = ssh_userauth_none(_this->m_srv_session, NULL);
// if (rc == SSH_AUTH_ERROR) {
// EXLOGE("[ssh] invalid password for password mode to login to real SSH server %s:%d.\n", _this->m_server_ip.c_str(), _this->m_server_port);
// _this->m_have_error = true;
// _this->m_retcode = SESS_STAT_ERR_AUTH_DENIED;
// return SSH_AUTH_ERROR;
// }
// // int auth_methods = ssh_userauth_list(_this->m_srv_session, NULL);
// const char* banner = ssh_get_issue_banner(_this->m_srv_session);
// if (NULL != banner) {
// EXLOGE("[ssh] issue banner: %s\n", banner);
// }
if (_this->m_auth_type == TP_AUTH_TYPE_PASSWORD) {
@ -350,7 +344,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
break;
int nprompts = ssh_userauth_kbdint_getnprompts(_this->m_srv_session);
if(0 == nprompts) {
if (0 == nprompts) {
rc = ssh_userauth_kbdint(_this->m_srv_session, NULL, NULL);
continue;
}
@ -364,7 +358,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
if (rc < 0) {
EXLOGE("[ssh] invalid password for interactive mode to login to real SSH server %s:%d.\n", _this->m_conn_ip.c_str(), _this->m_conn_port);
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_AUTH_DENIED;
_this->_session_error(TP_SESS_STAT_ERR_AUTH_DENIED);
return SSH_AUTH_ERROR;
}
}
@ -394,7 +388,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
EXLOGE("[ssh] can not use password mode or interactive mode to login to real SSH server %s:%d.\n", _this->m_conn_ip.c_str(), _this->m_conn_port);
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_AUTH_DENIED;
_this->_session_error(TP_SESS_STAT_ERR_AUTH_DENIED);
return SSH_AUTH_ERROR;
}
else if (_this->m_auth_type == TP_AUTH_TYPE_PRIVATE_KEY) {
@ -402,7 +396,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
if (SSH_OK != ssh_pki_import_privkey_base64(_this->m_acc_secret.c_str(), NULL, NULL, NULL, &key)) {
EXLOGE("[ssh] can not import private-key for auth.\n");
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_BAD_SSH_KEY;
_this->_session_error(TP_SESS_STAT_ERR_BAD_SSH_KEY);
return SSH_AUTH_ERROR;
}
@ -417,17 +411,18 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
else {
EXLOGE("[ssh] failed to use private-key to login to real SSH server %s:%d.\n", _this->m_conn_ip.c_str(), _this->m_conn_port);
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_AUTH_DENIED;
_this->_session_error(TP_SESS_STAT_ERR_AUTH_DENIED);
return SSH_AUTH_ERROR;
}
}
else if (_this->m_auth_type == TP_AUTH_TYPE_NONE) {
_this->_session_error(TP_SESS_STAT_ERR_AUTH_DENIED);
return SSH_AUTH_ERROR;
}
else {
EXLOGE("[ssh] invalid auth mode.\n");
_this->m_have_error = true;
_this->m_retcode = TP_SESS_STAT_ERR_AUTH_DENIED;
_this->_session_error(TP_SESS_STAT_ERR_AUTH_DENIED);
return SSH_AUTH_ERROR;
}
}
@ -436,14 +431,10 @@ ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userd
// 客户端尝试打开一个通道(然后才能通过这个通道发控制命令或者收发数据)
EXLOGV("[ssh] client open channel\n");
// TODO: 记录会话开始应该在这里进行这样可以为每一个通道记录不同的日志避免类似SecureCRT多标签页使用“复制会话”这样的功能将多个标签页中的记录混杂在一起。
// TODO: 每个通道应该记录单独的录像文件
SshSession *_this = (SshSession *)userdata;
ssh_channel cli_channel = ssh_channel_new(session);
if(cli_channel == NULL) {
if (cli_channel == NULL) {
EXLOGE("[ssh] can not create channel for client.\n");
return NULL;
}
@ -451,7 +442,7 @@ ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userd
// 我们也要向真正的服务器申请打开一个通道,来进行转发
ssh_channel srv_channel = ssh_channel_new(_this->m_srv_session);
if(srv_channel == NULL) {
if (srv_channel == NULL) {
EXLOGE("[ssh] can not create channel for server.\n");
return NULL;
}
@ -462,46 +453,51 @@ ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userd
}
ssh_set_channel_callbacks(srv_channel, &_this->m_srv_channel_cb);
TP_SSH_CHANNEL_PAIR* cp = new TP_SSH_CHANNEL_PAIR;
cp->type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
cp->cli_channel = cli_channel;
cp->srv_channel = srv_channel;
if (!_this->_on_session_begin(cp)) {
ssh_channel_close(cli_channel);
ssh_channel_free(cli_channel);
ssh_channel_close(srv_channel);
ssh_channel_free(srv_channel);
delete cp;
return NULL;
}
// 将客户端和服务端的通道关联起来
{
ExThreadSmartLock locker(_this->m_lock);
TS_SSH_CHANNEL_INFO *srv_info = new TS_SSH_CHANNEL_INFO;
srv_info->channel = srv_channel;
srv_info->type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
_this->m_channel_cli_srv.insert(std::make_pair(cli_channel, srv_info));
TS_SSH_CHANNEL_INFO *cli_info = new TS_SSH_CHANNEL_INFO;
cli_info->channel = cli_channel;
cli_info->type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
_this->m_channel_srv_cli.insert(std::make_pair(srv_channel, cli_info));
_this->m_channels.push_back(cp);
}
EXLOGD("[ssh] channel for client and server created.\n");
return cli_channel;
}
TS_SSH_CHANNEL_INFO *SshSession::_get_cli_channel(ssh_channel srv_channel) {
TP_SSH_CHANNEL_PAIR* SshSession::_get_channel_pair(int channel_side, ssh_channel channel) {
ExThreadSmartLock locker(m_lock);
ts_ssh_channel_map::iterator it = m_channel_srv_cli.find(srv_channel);
if (it == m_channel_srv_cli.end())
return NULL;
else
return it->second;
}
TS_SSH_CHANNEL_INFO *SshSession::_get_srv_channel(ssh_channel cli_channel) {
ExThreadSmartLock locker(m_lock);
ts_ssh_channel_map::iterator it = m_channel_cli_srv.find(cli_channel);
if (it == m_channel_cli_srv.end())
return NULL;
else
return it->second;
}
tp_channels::iterator it = m_channels.begin();
for (; it != m_channels.end(); ++it) {
if (channel_side == TP_SSH_CLIENT_SIDE) {
if ((*it)->cli_channel == channel)
return (*it);
}
else {
if ((*it)->srv_channel == channel)
return (*it);
}
}
void SshSession::_process_ssh_command(int from, const ex_u8* data, int len)
return NULL;
}
void SshSession::_process_ssh_command(TppSshRec* rec, int from, const ex_u8* data, int len)
{
if (TS_SSH_DATA_FROM_CLIENT == from)
if (TP_SSH_CLIENT_SIDE == from)
{
m_command_flag = 0;
@ -593,7 +589,7 @@ void SshSession::_process_ssh_command(int from, const ex_u8* data, int len)
ex_replace_all(str, "\n", "");
//EXLOGD("[ssh] save cmd: [%s]", str.c_str());
str += "\r\n";
m_rec.record_command(str);
rec->record_command(str);
}
m_cmd_char_list.clear();
m_cmd_char_pos = m_cmd_char_list.begin();
@ -611,7 +607,7 @@ void SshSession::_process_ssh_command(int from, const ex_u8* data, int len)
}
}
}
else if (TS_SSH_DATA_FROM_SERVER == from)
else if (TP_SSH_SERVER_SIDE == from)
{
if (m_command_flag == 0)
return;
@ -774,7 +770,7 @@ void SshSession::_process_ssh_command(int from, const ex_u8* data, int len)
return;
}
void SshSession::_process_sftp_command(const ex_u8* data, int len) {
void SshSession::_process_sftp_command(TppSshRec* rec, const ex_u8* data, int len) {
// SFTP protocol: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13
//EXLOG_BIN(data, len, "[sftp] client channel data");
@ -789,7 +785,7 @@ void SshSession::_process_sftp_command(const ex_u8* data, int len) {
if (sftp_cmd == 0x01) {
// 0x01 = 1 = SSH_FXP_INIT
m_rec.record_command("SFTP INITIALIZE\r\n");
rec->record_command("SFTP INITIALIZE\r\n");
return;
}
@ -864,101 +860,90 @@ void SshSession::_process_sftp_command(const ex_u8* data, int len) {
ex_strformat(msg, 2048, "%d:%s:%s:%s\r\n", sftp_cmd, act, str1.c_str(), str2.c_str());
}
m_rec.record_command(msg);
rec->record_command(msg);
}
int SshSession::_on_client_pty_request(ssh_session session, ssh_channel channel, const char *term, int x, int y, int px, int py, void *userdata) {
SshSession *_this = (SshSession *)userdata;
if (_this->m_is_sftp) {
EXLOGE("[ssh] try to request pty on a sftp-session.\n");
return SSH_ERROR;
}
EXLOGD("[ssh] client request terminal: %s, (%d, %d) / (%d, %d)\n", term, x, y, px, py);
_this->m_rec.record_win_size_startup(x, y);
TS_SSH_CHANNEL_INFO *info = _this->_get_srv_channel(channel);
if (NULL == info || NULL == info->channel) {
EXLOGE("[ssh] when client request pty, not found server channel.\n");
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when client request pty, not found channel pair.\n");
return SSH_ERROR;
}
return ssh_channel_request_pty_size(info->channel, term, x, y);
cp->rec.record_win_size_startup(x, y);
return ssh_channel_request_pty_size(cp->srv_channel, term, x, y);
}
int SshSession::_on_client_shell_request(ssh_session session, ssh_channel channel, void *userdata) {
SshSession *_this = (SshSession *)userdata;
if (_this->m_is_sftp) {
EXLOGE("[ssh] request shell on a sftp-session is denied.\n");
return SSH_ERROR;
}
EXLOGD("[ssh] client request shell\n");
TS_SSH_CHANNEL_INFO *srv_info = _this->_get_srv_channel(channel);
if (NULL == srv_info || NULL == srv_info->channel) {
EXLOGE("[ssh] when client request shell, not found server channel.\n");
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when client request shell, not found channel pair.\n");
return SSH_ERROR;
}
srv_info->type = TS_SSH_CHANNEL_TYPE_SHELL;
TS_SSH_CHANNEL_INFO *cli_info = _this->_get_cli_channel(srv_info->channel);
if (NULL == cli_info || NULL == cli_info->channel) {
EXLOGE("[ssh] when client request shell, not found client channel.\n");
return SSH_ERROR;
}
cli_info->type = TS_SSH_CHANNEL_TYPE_SHELL;
cp->type = TS_SSH_CHANNEL_TYPE_SHELL;
g_ssh_env.session_update(cp->db_id, TP_PROTOCOL_TYPE_SSH_SHELL, TP_SESS_STAT_STARTED);
// FIXME: if client is putty, it will block here. the following function will never return.
// at this time, can not write data to this channel. read from this channel with timeout, got 0 byte.
// I have no idea how to fix it... :(
return ssh_channel_request_shell(srv_info->channel);
return ssh_channel_request_shell(cp->srv_channel);
}
void SshSession::_on_client_channel_close(ssh_session session, ssh_channel channel, void *userdata) {
EXLOGD("[ssh] on_client_channel_close().\n");
SshSession *_this = (SshSession *)userdata;
TS_SSH_CHANNEL_INFO *info = _this->_get_srv_channel(channel);
if (NULL == info || NULL == info->channel) {
EXLOGW("[ssh] when close client channel, not found server channel, maybe it already closed.\n");
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when client channel close, not found channel pair.\n");
return;
}
if (!ssh_channel_is_eof(channel))
ssh_channel_send_eof(channel);
if (!ssh_channel_is_closed(channel))
ssh_channel_close(channel);
//ssh_channel_free(channel);
if (!ssh_channel_is_eof(info->channel))
ssh_channel_send_eof(info->channel);
if (!ssh_channel_is_closed(info->channel))
ssh_channel_close(info->channel);
//ssh_channel_free(info->channel);
if (cp->srv_channel == NULL) {
EXLOGW("[ssh] when client channel close, server-channel not exists.\n");
}
else {
if (!ssh_channel_is_closed(cp->srv_channel)) {
if (!ssh_channel_is_eof(cp->srv_channel)) {
//EXLOGD("[ssh] when client channel close, send eof to server-channel.\n");
ssh_channel_send_eof(cp->cli_channel);
}
{
ExThreadSmartLock locker(_this->m_lock);
//EXLOGD("[ssh] when client channel close, close server-channel.\n");
ssh_channel_close(cp->srv_channel);
ts_ssh_channel_map::iterator it = _this->m_channel_cli_srv.find(channel);
if (it != _this->m_channel_cli_srv.end()) {
delete it->second;
_this->m_channel_cli_srv.erase(it);
}
else {
EXLOGW("[ssh] when remove client channel, it not in charge.\n");
}
it = _this->m_channel_srv_cli.find(info->channel);
if (it != _this->m_channel_srv_cli.end()) {
delete it->second;
_this->m_channel_srv_cli.erase(it);
}
else {
EXLOGW("[ssh] when remove client channel, not found server channel.\n");
ssh_channel_free(cp->srv_channel);
cp->srv_channel = NULL;
}
}
if (!ssh_channel_is_closed(cp->cli_channel)) {
if (!ssh_channel_is_eof(cp->cli_channel)) {
//EXLOGD("[ssh] when client channel close, send eof to client-channel.\n");
ssh_channel_send_eof(cp->cli_channel);
}
//EXLOGD("[ssh] when client channel close, close client-channel.\n");
ssh_channel_close(cp->cli_channel);
ssh_channel_free(cp->cli_channel);
cp->cli_channel = NULL;
}
_this->_on_session_end(cp);
}
int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata)
@ -973,19 +958,25 @@ int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel
if (_this->m_recving_from_cli)
return 0;
TS_SSH_CHANNEL_INFO *info = _this->_get_srv_channel(channel);
if (NULL == info || NULL == info->channel) {
EXLOGE("[ssh] when receive client channel data, not found server channel.\n");
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when receive client channel data, not found channel pair.\n");
return SSH_ERROR;
}
// TS_SSH_CHANNEL_INFO *info = _this->_get_srv_channel(channel);
// if (NULL == info || NULL == info->channel) {
// EXLOGE("[ssh] when receive client channel data, not found server channel.\n");
// return SSH_ERROR;
// }
_this->m_recving_from_cli = true;
if (info->type == TS_SSH_CHANNEL_TYPE_SHELL)
if (cp->type == TS_SSH_CHANNEL_TYPE_SHELL)
{
try
{
_this->_process_ssh_command(TS_SSH_DATA_FROM_CLIENT, (ex_u8*)data, len);
_this->_process_ssh_command(&cp->rec, TP_SSH_CLIENT_SIDE, (ex_u8*)data, len);
}
catch (...)
{
@ -993,72 +984,84 @@ int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel
}
else
{
_this->_process_sftp_command((ex_u8*)data, len);
_this->_process_sftp_command(&cp->rec, (ex_u8*)data, len);
}
int ret = 0;
if (is_stderr)
ret = ssh_channel_write_stderr(info->channel, data, len);
ret = ssh_channel_write_stderr(cp->srv_channel, data, len);
else
ret = ssh_channel_write(info->channel, data, len);
if (ret <= 0)
EXLOGE("[ssh] send to server failed.\n");
ret = ssh_channel_write(cp->srv_channel, data, len);
if (ret == SSH_ERROR) {
EXLOGE("[ssh] send data(%dB) to server failed. [%d][cli:%s][srv:%s]\n", len, ret, ssh_get_error(_this->m_cli_session), ssh_get_error(_this->m_srv_session));
}
_this->m_recving_from_cli = false;
return len;
return ret;
}
int SshSession::_on_client_pty_win_change(ssh_session session, ssh_channel channel, int width, int height, int pxwidth, int pwheight, void *userdata) {
EXLOGD("[ssh] client pty win size change to: (%d, %d)\n", width, height);
SshSession *_this = (SshSession *)userdata;
TS_SSH_CHANNEL_INFO *info = _this->_get_srv_channel(channel);
if (NULL == info || NULL == info->channel) {
EXLOGE("[ssh] when client pty win change, not found server channel.\n");
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when client pty win change, not found channel pair.\n");
return SSH_ERROR;
}
_this->m_rec.record_win_size_change(width, height);
// TS_SSH_CHANNEL_INFO *info = _this->_get_srv_channel(channel);
// if (NULL == info || NULL == info->channel) {
// EXLOGE("[ssh] when client pty win change, not found server channel.\n");
// return SSH_ERROR;
// }
return ssh_channel_change_pty_size(info->channel, width, height);
cp->rec.record_win_size_change(width, height);
return ssh_channel_change_pty_size(cp->srv_channel, width, height);
}
int SshSession::_on_client_channel_subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata) {
EXLOGD("[ssh] on_client_channel_subsystem_request(): %s\n", subsystem);
SshSession *_this = (SshSession *)userdata;
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when request channel subsystem, not found channel pair.\n");
return SSH_ERROR;
}
// 目前只支持SFTP子系统
if (strcmp(subsystem, "sftp") != 0) {
EXLOGE("[ssh] support `sftp` subsystem only, but got `%s`.\n", subsystem);
_this->m_retcode = TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL;
cp->retcode = TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL;
return SSH_ERROR;
}
TS_SSH_CHANNEL_INFO *srv_info = _this->_get_srv_channel(channel);
if (NULL == srv_info || NULL == srv_info->channel) {
EXLOGE("[ssh] when receive client channel subsystem request, not found server channel.\n");
return SSH_ERROR;
}
srv_info->type = TS_SSH_CHANNEL_TYPE_SFTP;
// TS_SSH_CHANNEL_INFO *srv_info = _this->_get_srv_channel(channel);
// if (NULL == srv_info || NULL == srv_info->channel) {
// EXLOGE("[ssh] when receive client channel subsystem request, not found server channel.\n");
// return SSH_ERROR;
// }
// srv_info->type = TS_SSH_CHANNEL_TYPE_SFTP;
//
// TS_SSH_CHANNEL_INFO *cli_info = _this->_get_cli_channel(srv_info->channel);
// if (NULL == cli_info || NULL == cli_info->channel) {
// EXLOGE("[ssh] when client request shell, not found client channel.\n");
// return SSH_ERROR;
// }
// cli_info->type = TS_SSH_CHANNEL_TYPE_SFTP;
cp->type = TS_SSH_CHANNEL_TYPE_SFTP;
g_ssh_env.session_update(cp->db_id, TP_PROTOCOL_TYPE_SSH_SFTP, TP_SESS_STAT_STARTED);
TS_SSH_CHANNEL_INFO *cli_info = _this->_get_cli_channel(srv_info->channel);
if (NULL == cli_info || NULL == cli_info->channel) {
EXLOGE("[ssh] when client request shell, not found client channel.\n");
return SSH_ERROR;
}
cli_info->type = TS_SSH_CHANNEL_TYPE_SFTP;
// 一个ssh会话打开了sftp通道就将连接信息记录下来备用随后这个session-id再次尝试连接时我们允许其连接。
_this->_enter_sftp_mode();
//_this->_enter_sftp_mode();
return ssh_channel_request_subsystem(srv_info->channel, subsystem);
}
void SshSession::_enter_sftp_mode(void) {
if (!m_is_sftp) {
m_is_sftp = true;
m_proxy->add_sftp_session_info(m_sid, m_conn_ip, m_conn_port, m_acc_name, m_acc_secret, m_auth_type);
}
return ssh_channel_request_subsystem(cp->srv_channel, subsystem);
}
int SshSession::_on_client_channel_exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata) {
@ -1071,15 +1074,21 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
//EXLOG_BIN((ex_u8*)data, len, "on_server_channel_data [is_stderr=%d]:", is_stderr);
SshSession *_this = (SshSession *)userdata;
// return 0 means data not processed, so this function will be called with this data again.
if (_this->m_recving_from_cli)
return 0;
if (_this->m_recving_from_srv)
return 0;
TS_SSH_CHANNEL_INFO *info = _this->_get_cli_channel(channel);
if (NULL == info || NULL == info->channel) {
EXLOGE("[ssh] when receive server channel data, not found client channel.\n");
_this->m_retcode = TP_SESS_STAT_ERR_INTERNAL;
// TS_SSH_CHANNEL_INFO *info = _this->_get_cli_channel(channel);
// if (NULL == info || NULL == info->channel) {
// EXLOGE("[ssh] when receive server channel data, not found client channel.\n");
// _this->m_retcode = TP_SESS_STAT_ERR_INTERNAL;
// return SSH_ERROR;
// }
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_SERVER_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when receive server channel data, not found channel pair.\n");
return SSH_ERROR;
}
@ -1087,7 +1096,7 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
// TODO: hard code not good... :(
// 偶尔某次操作会导致ssh_session->session_state为SSH_SESSION_STATE_ERROR
// 但是将其强制改为SSH_SESSION_STATE_AUTHENTICATED后续操作仍然能成功主要在向客户端发送第一包数据时
ex_u8* _t = (ex_u8*)(ssh_channel_get_session(info->channel));
ex_u8* _t = (ex_u8*)(ssh_channel_get_session(cp->cli_channel));
if (_t[1116] == 9) // SSH_SESSION_STATE_AUTHENTICATED = 8, SSH_SESSION_STATE_ERROR = 9
{
EXLOGW(" --- [ssh] hard code to fix client connect session error state.\n");
@ -1097,12 +1106,13 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
_this->m_recving_from_srv = true;
if (info->type == TS_SSH_CHANNEL_TYPE_SHELL && !is_stderr)
if (cp->type == TS_SSH_CHANNEL_TYPE_SHELL && !is_stderr)
{
try
{
_this->_process_ssh_command(TS_SSH_DATA_FROM_SERVER, (ex_u8*)data, len);
_this->m_rec.record(TS_RECORD_TYPE_SSH_DATA, (unsigned char *)data, len);
_this->_process_ssh_command(&cp->rec, TP_SSH_SERVER_SIDE, (ex_u8*)data, len);
//_this->m_rec.record(TS_RECORD_TYPE_SSH_DATA, (unsigned char *)data, len);
cp->rec.record(TS_RECORD_TYPE_SSH_DATA, (unsigned char *)data, len);
}
catch (...)
{
@ -1118,7 +1128,7 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
{
_this->m_is_first_server_data = false;
if (info->type != TS_SSH_CHANNEL_TYPE_SFTP)
if (cp->type != TS_SSH_CHANNEL_TYPE_SFTP)
{
char buf[256] = { 0 };
@ -1140,7 +1150,7 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
"\r\n",
_this->m_conn_ip.c_str(),
_this->m_conn_port, auth_mode
);
);
int buf_len = strlen(buf);
ex_bin _data;
@ -1148,7 +1158,7 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
memcpy(&_data[0], buf, buf_len);
memcpy(&_data[buf_len], data, len);
ret = ssh_channel_write(info->channel, &_data[0], _data.size());
ret = ssh_channel_write(cp->cli_channel, &_data[0], _data.size());
_this->m_recving_from_srv = false;
return len;
@ -1157,11 +1167,12 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
#endif
if (is_stderr)
ret = ssh_channel_write_stderr(info->channel, data, len);
ret = ssh_channel_write_stderr(cp->cli_channel, data, len);
else
ret = ssh_channel_write(info->channel, data, len);
ret = ssh_channel_write(cp->cli_channel, data, len);
if (ret == SSH_ERROR) {
EXLOGE("[ssh] send data(%dB) to client failed (2). [%d][%s][%s]\n", len, ret, ssh_get_error(_this->m_cli_session), ssh_get_error(_this->m_cli_session));
EXLOGE("[ssh] send data(%dB) to client failed. [%d][cli:%s][srv:%s]\n", len, ret, ssh_get_error(_this->m_cli_session), ssh_get_error(_this->m_srv_session));
}
_this->m_recving_from_srv = false;
@ -1172,43 +1183,44 @@ void SshSession::_on_server_channel_close(ssh_session session, ssh_channel chann
EXLOGD("[ssh] on_server_channel_close().\n");
SshSession *_this = (SshSession *)userdata;
TS_SSH_CHANNEL_INFO *info = _this->_get_cli_channel(channel);
if (NULL == info || NULL == info->channel) {
EXLOGW("[ssh] when server channel close, not found client channel, maybe it already closed.\n");
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_SERVER_SIDE, channel);
if (NULL == cp) {
EXLOGE("[ssh] when server channel close, not found channel pair.\n");
return;
}
if (!ssh_channel_is_eof(channel))
ssh_channel_send_eof(channel);
if (!ssh_channel_is_closed(channel))
ssh_channel_close(channel);
//ssh_channel_free(channel);
// will the server-channel exist, the client-channel must exist too.
if (cp->cli_channel == NULL) {
EXLOGE("[ssh] when server channel close, client-channel not exists.\n");
}
else {
if (!ssh_channel_is_closed(cp->cli_channel)) {
if (!ssh_channel_is_eof(cp->cli_channel)) {
//EXLOGD("[ssh] when server channel close, send eof to client-channel.\n");
ssh_channel_send_eof(cp->cli_channel);
}
if (!ssh_channel_is_eof(info->channel))
ssh_channel_send_eof(info->channel);
if (!ssh_channel_is_closed(info->channel))
ssh_channel_close(info->channel);
//ssh_channel_free(info->channel);
//EXLOGD("[ssh] when server channel close, close client-channel.\n");
ssh_channel_close(cp->cli_channel);
{
ExThreadSmartLock locker(_this->m_lock);
ts_ssh_channel_map::iterator it = _this->m_channel_srv_cli.find(channel);
if (it != _this->m_channel_srv_cli.end()) {
delete it->second;
_this->m_channel_srv_cli.erase(it);
}
else {
EXLOGW("[ssh] when remove server channel, it not in charge..\n");
}
it = _this->m_channel_cli_srv.find(info->channel);
if (it != _this->m_channel_cli_srv.end()) {
delete it->second;
_this->m_channel_cli_srv.erase(it);
}
else {
EXLOGW("[ssh] when remove server channel, not found client channel.\n");
ssh_channel_free(cp->cli_channel);
cp->cli_channel = NULL;
}
}
if (!ssh_channel_is_closed(cp->srv_channel)) {
if (!ssh_channel_is_eof(cp->srv_channel)) {
//EXLOGD("[ssh] when server channel close, send eof to server-channel.\n");
ssh_channel_send_eof(cp->srv_channel);
}
//EXLOGD("[ssh] when server channel close, close server-channel.\n");
ssh_channel_close(cp->srv_channel);
ssh_channel_free(cp->srv_channel);
cp->srv_channel = NULL;
}
_this->_on_session_end(cp);
}

View File

@ -17,24 +17,33 @@
#define TS_SSH_CHANNEL_TYPE_SHELL 1
#define TS_SSH_CHANNEL_TYPE_SFTP 2
#define TS_SSH_DATA_FROM_CLIENT 1
#define TS_SSH_DATA_FROM_SERVER 2
typedef struct TS_SSH_CHANNEL_INFO
{
int type; // TS_SSH_CHANNEL_TYPE_SHELL or TS_SSH_CHANNEL_TYPE_SFTP
ssh_channel channel;
TS_SSH_CHANNEL_INFO()
{
type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
channel = NULL;
}
}TS_SSH_CHANNEL_INFO;
typedef std::map<ssh_channel, TS_SSH_CHANNEL_INFO*> ts_ssh_channel_map;
#define TP_SSH_CLIENT_SIDE 1
#define TP_SSH_SERVER_SIDE 2
class SshProxy;
class SshSession;
class TP_SSH_CHANNEL_PAIR {
friend class SshSession;
public:
TP_SSH_CHANNEL_PAIR();
private:
int type; // TS_SSH_CHANNEL_TYPE_SHELL or TS_SSH_CHANNEL_TYPE_SFTP
ssh_channel cli_channel;
ssh_channel srv_channel;
TppSshRec rec;
int retcode;
int db_id;
};
typedef std::list<TP_SSH_CHANNEL_PAIR*> tp_channels;
class SshSession : public ExThreadBase
{
@ -44,9 +53,7 @@ public:
SshProxy* get_proxy(void) { return m_proxy; }
TS_SSH_CHANNEL_INFO* _get_cli_channel(ssh_channel srv_channel);
TS_SSH_CHANNEL_INFO* _get_srv_channel(ssh_channel cli_channel);
TP_SSH_CHANNEL_PAIR* _get_channel_pair(int channel_side, ssh_channel channel);
void client_ip(const char* ip) { m_client_ip = ip; }
const char* client_ip(void) const { return m_client_ip.c_str(); }
@ -56,24 +63,22 @@ public:
void save_record();
protected:
// 继承自 TppSessionBase
bool _on_session_begin(const TPP_CONNECT_INFO* info);
bool _on_session_end(void);
void _thread_loop(void);
void _set_stop_flag(void);
void _process_ssh_command(int from, const ex_u8* data, int len);
void _process_sftp_command(const ex_u8* data, int len);
void _session_error(int err_code);
bool _on_session_begin(TP_SSH_CHANNEL_PAIR* cp);
void _on_session_end(TP_SSH_CHANNEL_PAIR* cp);
void _process_ssh_command(TppSshRec* rec, int from, const ex_u8* data, int len);
void _process_sftp_command(TppSshRec* rec, const ex_u8* data, int len);
private:
void _run(void);
void _close_channels(void);
void _enter_sftp_mode(void);
static int _on_auth_password_request(ssh_session session, const char *user, const char *password, void *userdata);
static ssh_channel _on_new_channel_request(ssh_session session, void *userdata);
static int _on_client_pty_request(ssh_session session, ssh_channel channel, const char *term, int x, int y, int px, int py, void *userdata);
@ -89,10 +94,6 @@ private:
static void _on_server_channel_close(ssh_session session, ssh_channel channel, void* userdata);
private:
int m_retcode;
int m_db_id;
TppSshRec m_rec;
SshProxy* m_proxy;
ssh_session m_cli_session;
@ -103,6 +104,8 @@ private:
ex_astr m_client_ip;
ex_u16 m_client_port;
TPP_CONNECT_INFO* m_conn_info;
ex_astr m_sid;
ex_astr m_conn_ip;
ex_u16 m_conn_port;
@ -111,12 +114,10 @@ private:
int m_auth_type;
bool m_is_first_server_data;
bool m_is_sftp;
bool m_is_logon;
// Ò»¸össh_sessionÖпÉÒÔ´ò¿ª¶à¸össh_channel
ts_ssh_channel_map m_channel_cli_srv; // 通过客户端通道查找服务端通道
ts_ssh_channel_map m_channel_srv_cli; // 通过服务端通道查找客户端通道
tp_channels m_channels;
bool m_have_error;

View File

@ -23,8 +23,8 @@ $app.create_controls = function (cb_stack) {
var table_record_options = {
dom_id: 'table-record',
data_source: {
type: 'ajax-post',
url: '/audit/get-records',
type: 'ajax-post'
,url: '/audit/get-records'
//exclude: {'state': [TP_SESS_STAT_RUNNING, TP_SESS_STAT_STARTED]}
},
column_default: {sort: false, align: 'left'},
@ -42,15 +42,13 @@ $app.create_controls = function (cb_stack) {
{
title: 'ID',
key: 'id',
// sort: true,
// sort_asc: false,
sort: true,
sort_asc: false,
fields: {id: 'id'}
},
{
title: '会话ID',
key: 'sid',
// sort: true,
// sort_asc: false,
render: 'sid',
width: 60,
fields: {sid: 'sid'}
@ -94,8 +92,8 @@ $app.create_controls = function (cb_stack) {
{
title: '开始时间',
key: 'time_begin',
sort: true,
sort_asc: false,
// sort: true,
// sort_asc: false,
render: 'time_begin',
fields: {time_begin: 'time_begin'}
},
@ -162,19 +160,6 @@ $app.create_controls = function (cb_stack) {
$app.dom.btn_refresh_record.click(function () {
$app.table_record.load_data();
});
// $app.dom.chkbox_host_select_all.click(function () {
// var _objects = $('#' + $app.table_record.dom_id + ' tbody').find('[data-check-box]');
// if ($(this).is(':checked')) {
// $.each(_objects, function (i, _obj) {
// $(_obj).prop('checked', true);
// });
// } else {
// $.each(_objects, function (i, _obj) {
// $(_obj).prop('checked', false);
// });
// }
// });
//$app.dom.btn_remove_record.click($app.on_btn_remove_record_click);
cb_stack.exec();
};
@ -273,9 +258,9 @@ $app.on_table_host_render_created = function (render) {
case 200:
return '<span class="label label-success">SSH</span>';
case 201:
return '<span class="label label-success">SFTP</span>';
return '<span class="label label-info">SFTP</span>';
case 300:
return '<span class="label label-success">TELNET</span>';
return '<span class="label label-warning">TELNET</span>';
default:
return '<span class="label label-danger">未知</span>';
}
@ -371,9 +356,9 @@ $app.on_table_host_render_created = function (render) {
if (fields.state >= TP_SESS_STAT_STARTED || fields.state === TP_SESS_STAT_ERR_RESET) {
if (fields.state === TP_SESS_STAT_STARTED) {
ret.push('<a href="javascript:;" class="btn btn-sm btn-warning" data-action="sync" data-record-id="' + fields.id + '"><i class="fa fa-clone fa-fw"></i> 同步</a>&nbsp');
//ret.push('<a href="javascript:;" class="btn btn-sm btn-warning" data-action="sync" data-record-id="' + fields.id + '"><i class="fa fa-clone fa-fw"></i> 同步</a>&nbsp');
} else {
// if (fields.protocol_sub_type !== TP_PROTOCOL_TYPE_SSH_SFTP)
if (fields.protocol_sub_type !== TP_PROTOCOL_TYPE_SSH_SFTP)
ret.push('<a href="javascript:;" class="btn btn-sm btn-primary" data-action="replay" data-record-id="' + fields.id + '"><i class="fa fa-caret-square-o-right fa-fw"></i> 回放</a>&nbsp');
}
if (fields.protocol_sub_type !== TP_PROTOCOL_TYPE_RDP_DESKTOP) {
@ -395,8 +380,6 @@ $app.on_table_host_header_created = function (header) {
// 表格内嵌过滤器的事件绑定在这时进行(也可以延期到整个表格创建完成时进行)
header._table_ctrl.get_filter_ctrl('search').on_created();
// header._table_ctrl.get_filter_ctrl('role').on_created();
// header._table_ctrl.get_filter_ctrl('state').on_created();
};
$app.get_selected_record = function (tbl) {

View File

@ -40,6 +40,8 @@ $app.req_record_data = function (record_id, offset) {
$app.req_record_data(record_id, g_data_offset);
}
} else {
$app.dom.status.text("读取录像数据失败:" + tp_error_msg(ret.code));
$tp.notify_error('读取录像数据失败:' + tp_error_msg(ret.code, ret.message));
console.log('req_record_info error ', ret.code);
}
},
@ -76,16 +78,17 @@ $app.on_init = function (cb_stack) {
function (ret) {
if (ret.code === TPE_OK) {
g_header = ret.data;
// console.log('header', g_header);
console.log('header', g_header);
$('#recorder-info').html(tp_format_datetime(g_header.start) + ': ' + g_header.user_name + '@' + g_header.client_ip + ' 访问 ' + g_header.account + '@' + g_header.conn_ip + ':' + g_header.conn_port);
$app.req_record_data(record_id, 0);
g_current_time = 0;
setTimeout(init, 1000);
//setTimeout(init, 500);
init_and_play();
} else {
$tp.notify_error('请求录像数据失败:' + tp_error_msg(ret.code, ret.message));
$tp.notify_error('读取录像信息失败:' + tp_error_msg(ret.code, ret.message));
console.error('load init info error ', ret.code);
}
},
@ -161,9 +164,9 @@ $app.on_init = function (cb_stack) {
pause();
});
$app.dom.progress.mouseup(function () {
// console.log(g_current_time);
g_current_time = parseInt(g_header.time_used * $app.dom.progress.val() / 100);
setTimeout(function () {
init();
init_and_play();
}, 100);
});
$app.dom.progress.mousemove(function () {
@ -178,7 +181,7 @@ $app.on_init = function (cb_stack) {
}
};
function init() {
function init_and_play() {
if (_.isNull(g_console_term)) {
g_console_term = new Terminal({
cols: g_header.width,
@ -198,18 +201,22 @@ $app.on_init = function (cb_stack) {
g_console_term.reset(g_header.width, g_header.height);
}
if(g_header.pkg_count === 0)
return;
$app.dom.progress.val(0);
$app.dom.status.text("正在播放");
// $app.dom.status.text("正在播放");
$app.dom.btn_play.children().removeClass().addClass('fa fa-pause').text(' 暂停');
g_need_stop = false;
g_playing = true;
g_finish = false;
g_played_pkg_count = 0;
setTimeout(done, g_record_tick);
//setTimeout(do_play, g_record_tick);
do_play();
}
function done() {
function do_play() {
if (g_need_stop) {
g_playing = false;
return;
@ -217,7 +224,7 @@ $app.on_init = function (cb_stack) {
if (g_data.length <= g_played_pkg_count) {
$app.dom.status.text("正在缓存数据...");
g_timer = setTimeout(done, g_record_tick);
g_timer = setTimeout(do_play, g_record_tick);
return;
}
@ -286,7 +293,7 @@ $app.on_init = function (cb_stack) {
$app.dom.btn_play.children().removeClass().addClass('fa fa-play').text(' 播放');
} else {
if (!g_need_stop)
g_timer = setTimeout(done, _record_tick);
g_timer = setTimeout(do_play, _record_tick);
}
}
@ -304,7 +311,7 @@ $app.on_init = function (cb_stack) {
g_need_stop = false;
g_playing = true;
g_timer = setTimeout(done, g_record_tick);
g_timer = setTimeout(do_play, g_record_tick);
}
function pause() {
@ -320,7 +327,7 @@ $app.on_init = function (cb_stack) {
if (!_.isNull(g_timer))
clearTimeout(g_timer);
g_current_time = 0;
init();
init_and_play();
}
cb_stack.exec();

View File

@ -32,7 +32,7 @@ $app.create_controls = function (cb_stack) {
// title: '<input type="checkbox" id="user-list-select-all" value="">',
title: '',
key: 'chkbox',
sort: false,
//sort: false,
width: 36,
align: 'center',
render: 'make_check_box',
@ -41,6 +41,8 @@ $app.create_controls = function (cb_stack) {
{
title: 'ID',
key: 'id',
sort: true,
sort_asc: false,
fields: {id: 'id'}
},
{

View File

@ -90,10 +90,16 @@ class RpcHandler(TPBaseJsonHandler):
return self.write_json(TPE_OK, data={'rid': record_id})
def _session_update(self, param):
if 'rid' not in param or 'code' not in param:
try:
rid = param['rid']
protocol_sub_type = param['protocol_sub_type']
code = param['code']
except:
return self.write_json(TPE_PARAM)
if 'rid' not in param or 'code' not in param :
return self.write_json(TPE_PARAM)
if not record.session_update(param['rid'], param['code']):
if not record.session_update(rid, protocol_sub_type, code):
return self.write_json(TPE_DATABASE, 'can not write database.')
else:
return self.write_json(TPE_OK)

View File

@ -293,9 +293,9 @@ def session_begin(sid, user_id, host_id, acc_id, user_username, acc_username, ho
return TPE_OK, record_id
def session_update(record_id, state):
def session_update(record_id, protocol_sub_type, state):
db = get_db()
sql = 'UPDATE `{}record` SET state={} WHERE id={};'.format(db.table_prefix, int(state), int(record_id))
sql = 'UPDATE `{}record` SET protocol_sub_type={}, state={} WHERE id={};'.format(db.table_prefix, protocol_sub_type, int(state), int(record_id))
return db.exec(sql)