增强对SSHv1的支持,例如不允许打开SFTP、不允许建立多个通道等等。与客户端直连远程主机依然有一些差异,因为客户端连接TP时总是使用的SSHv2协议,所以无法准确判断远程主机的协议版本从而导致一些行为上的差异。

pull/105/head
Apex Liu 2017-11-30 01:25:31 +08:00
parent c5af4d0ad2
commit 711cb139e7
2 changed files with 55 additions and 57 deletions

View File

@ -214,24 +214,6 @@ void SshSession::_check_channels() {
m_channels.erase(it++); m_channels.erase(it++);
} }
else { else {
// if ((*it)->type == TS_SSH_CHANNEL_TYPE_SHELL) {
// // 尝试从服务器读取点东西
// ex_u8 buf[4096] = { 0 };
// int len = 0;
//
// len = ssh_channel_read_nonblocking(srv, buf, 4096, 0);
// if (len < 0) {
// EXLOGE("[ssh] -- try read failed. srv:err:%s\n", ssh_get_error(m_srv_session));
// EXLOGE("[ssh] -- try read failed. cli:err:%s\n", ssh_get_error(m_cli_session));
// ssh_channel_close(srv);
// }
// else if(len > 0) {
// EXLOGD("[ssh] -- try read server ok.\n");
// EXLOG_BIN(buf, len, "RECV\n");
// _on_server_channel_data(m_srv_session, srv, buf, len, 0, this);
// }
// }
++it; ++it;
} }
} }
@ -342,13 +324,10 @@ void SshSession::_run(void) {
// 如果是SSHv2 // 现在双方的连接已经建立好了,开始转发
//if (m_ssh_ver == 2) {
// 现在双方的连接已经建立好了,开始转发
ssh_event_add_session(event_loop, m_srv_session); ssh_event_add_session(event_loop, m_srv_session);
do { do {
err = ssh_event_dopoll(event_loop, 5000); err = ssh_event_dopoll(event_loop, 5000);
//EXLOGD("ssh_event_dopoll() return %d.\n", r);
if (err == SSH_ERROR) { if (err == SSH_ERROR) {
if (0 != ssh_get_error_code(m_cli_session)) if (0 != ssh_get_error_code(m_cli_session))
{ {
@ -380,14 +359,13 @@ void SshSession::_run(void) {
ssh_event_remove_session(event_loop, m_cli_session); ssh_event_remove_session(event_loop, m_cli_session);
ssh_event_remove_session(event_loop, m_srv_session); ssh_event_remove_session(event_loop, m_srv_session);
ssh_event_free(event_loop); ssh_event_free(event_loop);
//}
// TODO: 这里还是有问题如果一边是走SSHv1另一边是SSHv2放在同一个event_loop时SSHv1会收不到数据放到循环中时SSHv2得不到数据 // 如果一边是走SSHv1另一边是SSHv2放在同一个event_loop时SSHv1会收不到数据放到循环中时SSHv2得不到数据
// 所以当SSHv1的远程主机连接后到建立好shell环境之后就进入另一种读取数据的循环不再使用ssh_event_dopoll()了。
if (m_ssh_ver == 1) if (m_ssh_ver == 1)
{ {
tp_channels::iterator it = m_channels.begin(); // SSHv1Ö»ÄÜ´ò¿ªÒ»¸öchannel tp_channels::iterator it = m_channels.begin(); // SSHv1Ö»ÄÜ´ò¿ªÒ»¸öchannel
ssh_channel cli = (*it)->cli_channel; ssh_channel cli = (*it)->cli_channel;
ssh_channel srv = (*it)->srv_channel; ssh_channel srv = (*it)->srv_channel;
@ -399,31 +377,32 @@ void SshSession::_run(void) {
if (ok) { if (ok) {
len = ssh_channel_read_nonblocking(cli, buf, 4096, 0); len = ssh_channel_read_nonblocking(cli, buf, 4096, 0);
if (len < 0) { if (len < 0)
EXLOGE("[ssh] -- try read failed. srv:err:%s\n", ssh_get_error(m_srv_session));
EXLOGE("[ssh] -- try read failed. cli:err:%s\n", ssh_get_error(m_cli_session));
ok = false; ok = false;
} else if (len > 0)
else if (len > 0) {
EXLOGD("[ssh] -- try read client ok.\n");
//EXLOG_BIN(buf, len, "RECV\n");
_on_client_channel_data(m_cli_session, cli, buf, len, 0, this); _on_client_channel_data(m_cli_session, cli, buf, len, 0, this);
}
len = ssh_channel_read_nonblocking(cli, buf, 4096, 1);
if (len < 0)
ok = false;
else if (len > 0)
_on_client_channel_data(m_cli_session, cli, buf, len, 1, this);
len = ssh_channel_read_nonblocking(srv, buf, 4096, 0); len = ssh_channel_read_nonblocking(srv, buf, 4096, 0);
if (len < 0) { if (len < 0)
EXLOGE("[ssh] -- try read failed. srv:err:%s\n", ssh_get_error(m_srv_session));
EXLOGE("[ssh] -- try read failed. cli:err:%s\n", ssh_get_error(m_cli_session));
ok = false; ok = false;
} else if (len > 0)
else if (len > 0) {
EXLOGD("[ssh] -- try read server ok.\n");
//EXLOG_BIN(buf, len, "RECV\n");
_on_server_channel_data(m_srv_session, srv, buf, len, 0, this); _on_server_channel_data(m_srv_session, srv, buf, len, 0, this);
}
if (!ok) len = ssh_channel_read_nonblocking(srv, buf, 4096, 1);
if (len < 0)
ok = false;
else if (len > 0)
_on_server_channel_data(m_srv_session, srv, buf, len, 1, this);
if (!ok) {
_close_channels(); _close_channels();
}
} }
if (!ok) { if (!ok) {
@ -434,6 +413,8 @@ void SshSession::_run(void) {
ex_sleep_ms(30); ex_sleep_ms(30);
} while (m_channels.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);
} }
} }
@ -514,7 +495,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user,
_timeout = 10; // 10 sec. _timeout = 10; // 10 sec.
ssh_options_set(_this->m_srv_session, SSH_OPTIONS_TIMEOUT, &_timeout); ssh_options_set(_this->m_srv_session, SSH_OPTIONS_TIMEOUT, &_timeout);
// TODO: 获取服务端ssh版本是v1还是v2 // 获取服务端ssh版本是v1还是v2
_this->m_ssh_ver = ssh_get_version(_this->m_srv_session); _this->m_ssh_ver = ssh_get_version(_this->m_srv_session);
EXLOGW("[ssh] real host is SSHv%d\n", _this->m_ssh_ver); EXLOGW("[ssh] real host is SSHv%d\n", _this->m_ssh_ver);
@ -668,6 +649,13 @@ ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userd
SshSession *_this = (SshSession *)userdata; SshSession *_this = (SshSession *)userdata;
// TODO: 客户端与TP连接使用的总是SSHv2协议因为最开始连接时还不知道真正的远程主机是不是SSHv1。
// 因此此处行为与客户端直连远程主机有些不一样。直连时SecureCRT的克隆会话功能会因为以为连接的是SSHv1而自动重新连接而不是打开新通道。
if (_this->m_ssh_ver == 1 && _this->m_channels.size() != 0) {
EXLOGE("[ssh] SSH1 supports only one execution channel. One has already been opened.\n");
return NULL;
}
ssh_channel cli_channel = ssh_channel_new(session); 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"); EXLOGE("[ssh] can not create channel for client.\n");
@ -803,7 +791,7 @@ void SshSession::_on_client_channel_close(ssh_session session, ssh_channel chann
int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata) int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata)
{ {
//EXLOG_BIN((ex_u8*)data, len, "on_client_channel_data [is_stderr=%d]:", is_stderr); //EXLOG_BIN((ex_u8*)data, len, " ---> on_client_channel_data [is_stderr=%d]:", is_stderr);
SshSession *_this = (SshSession *)userdata; SshSession *_this = (SshSession *)userdata;
@ -885,6 +873,12 @@ int SshSession::_on_client_channel_subsystem_request(ssh_session session, ssh_ch
EXLOGD("[ssh] on_client_channel_subsystem_request(): %s\n", subsystem); EXLOGD("[ssh] on_client_channel_subsystem_request(): %s\n", subsystem);
SshSession *_this = (SshSession *)userdata; SshSession *_this = (SshSession *)userdata;
if (_this->m_ssh_ver == 1) {
// SSHv1 not support subsystem, so some client like WinSCP will use shell-mode instead.
EXLOGE("[ssh] real host running on SSHv1, does not support subsystem `%s`.\n", subsystem);
return SSH_ERROR;
}
TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel); TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel);
if (NULL == cp) { if (NULL == cp) {
EXLOGE("[ssh] when request channel subsystem, not found channel pair.\n"); EXLOGE("[ssh] when request channel subsystem, not found channel pair.\n");
@ -915,7 +909,7 @@ int SshSession::_on_client_channel_exec_request(ssh_session session, ssh_channel
} }
int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata) { int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata) {
//EXLOG_BIN((ex_u8*)data, len, "on_server_channel_data [is_stderr=%d]:", is_stderr); //EXLOG_BIN((ex_u8*)data, len, " <--- on_server_channel_data [is_stderr=%d]:", is_stderr);
SshSession *_this = (SshSession *)userdata; SshSession *_this = (SshSession *)userdata;
@ -931,17 +925,17 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
return SSH_ERROR; return SSH_ERROR;
} }
#ifdef EX_OS_WIN32 // #ifdef EX_OS_WIN32
// TODO: hard code not good... :( // // TODO: hard code not good... :(
// 偶尔某次操作会导致ssh_session->session_state为SSH_SESSION_STATE_ERROR // // 偶尔某次操作会导致ssh_session->session_state为SSH_SESSION_STATE_ERROR
// 但是将其强制改为SSH_SESSION_STATE_AUTHENTICATED后续操作仍然能成功主要在向客户端发送第一包数据时 // // 但是将其强制改为SSH_SESSION_STATE_AUTHENTICATED后续操作仍然能成功主要在向客户端发送第一包数据时
ex_u8* _t = (ex_u8*)(ssh_channel_get_session(cp->cli_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 // 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"); // EXLOGW(" --- [ssh] hard code to fix client connect session error state.\n");
_t[1116] = 8; // _t[1116] = 8;
} // }
#endif // #endif
_this->m_recving_from_srv = true; _this->m_recving_from_srv = true;

View File

@ -78,6 +78,10 @@ $app.on_init = function (cb_stack) {
function (ret) { function (ret) {
if (ret.code === TPE_OK) { if (ret.code === TPE_OK) {
g_header = ret.data; g_header = ret.data;
if(g_header.width === 0)
g_header.width = 80;
if(g_header.height === 0)
g_header.height = 24;
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); $('#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);