telnet录像支持记录客户端窗口大小改变事件,并能正确回放了。

pull/105/head
Apex Liu 2018-04-08 17:01:45 +08:00
parent 21e07f7b32
commit 3aa873d41e
5 changed files with 202 additions and 92 deletions

View File

@ -88,7 +88,7 @@ void TelnetConn::_on_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *bu
else {
// #ifdef LOG_DATA
// if(!_this->m_session->is_relay())
// EXLOG_BIN((ex_u8*)buf->base, nread, "[telnet] [%s] RECV %d.", _this->m_name, nread);
// EXLOG_BIN((ex_u8*)buf->base, nread, "[telnet] [%s] RECV %d.", _this->m_name, nread);
// #endif
}
@ -109,7 +109,7 @@ bool TelnetConn::send(const ex_u8 *data, size_t size) {
bool TelnetConn::_raw_send(const ex_u8 *data, size_t size) {
// #ifdef LOG_DATA
// if (!m_session->is_relay())
// EXLOG_BIN(data, size, "[telnet] [%s] SEND %dB.", m_name, size);
// EXLOG_BIN(data, size, "[telnet] [%s] SEND %dB.", m_name, size);
// #endif
uv_write_t *w = (uv_write_t *) calloc(1, sizeof(uv_write_t));

View File

@ -85,13 +85,28 @@ void TppTelnetRec::record(ex_u8 type, const ex_u8* data, size_t size)
m_header_changed = true;
}
void TppTelnetRec::record_win_size(int width, int height)
{
m_head.basic.width = (ex_u16)width;
m_head.basic.height = (ex_u16)height;
m_save_full_header = true;
m_header_changed = true;
}
// void TppTelnetRec::record_win_size(int width, int height)
// {
// m_head.basic.width = (ex_u16)width;
// m_head.basic.height = (ex_u16)height;
// m_save_full_header = true;
// m_header_changed = true;
// }
void TppTelnetRec::record_win_size_startup(int width, int height)
{
m_head.basic.width = (ex_u16)width;
m_head.basic.height = (ex_u16)height;
m_save_full_header = true;
}
void TppTelnetRec::record_win_size_change(int width, int height)
{
TS_RECORD_WIN_SIZE pkg = { 0 };
pkg.width = (ex_u16)width;
pkg.height = (ex_u16)height;
record(TS_RECORD_TYPE_TELNET_TERM_SIZE, (ex_u8*)&pkg, sizeof(TS_RECORD_WIN_SIZE));
}
bool TppTelnetRec::_save_to_info_file() {
if (!m_header_changed)

View File

@ -24,7 +24,9 @@ public:
virtual ~TppTelnetRec();
void record(ex_u8 type, const ex_u8* data, size_t size);
void record_win_size(int width, int height);
// void record_win_size(int width, int height);
void record_win_size_startup(int width, int height);
void record_win_size_change(int width, int height);
void save_record();

View File

@ -3,13 +3,13 @@
#include "tpp_env.h"
#include <teleport_const.h>
#define TELNET_IAC 255
#define TELNET_DONT 254
#define TELNET_DO 253
#define TELNET_WONT 252
#define TELNET_WILL 251
#define TELNET_SB 250
#define TELNET_SE 240
#define TELNET_IAC 0xFF
#define TELNET_DONT 0xFE
#define TELNET_DO 0xFD
#define TELNET_WONT 0xFC
#define TELNET_WILL 0xFB
#define TELNET_SB 0xFA
#define TELNET_SE 0xF0
TelnetSession::TelnetSession(TelnetProxy *proxy) :
@ -21,9 +21,12 @@ TelnetSession::TelnetSession(TelnetProxy *proxy) :
m_db_id = 0;
m_is_relay = false;
m_is_closed = false;
m_first_client_pkg = true;
m_win_width = 80;
m_win_height = 24;
m_is_putty_mode = false;
m_is_putty_eat_username = false;
m_username_sent = false;
m_password_sent = false;
@ -212,6 +215,32 @@ sess_state TelnetSession::_do_negotiation_with_client(TelnetConn* conn) {
if (0 == conn->data().size())
return s_negotiation_with_client;
if (m_first_client_pkg) {
m_first_client_pkg = false;
MemBuffer& mbuf = conn->data();
if (mbuf.size() > 14 && 0 == memcmp(mbuf.data(), "session:", 8)) {
m_is_putty_mode = false;
mbuf.pop(8);
for (; mbuf.size() > 0; ) {
if (mbuf.data()[mbuf.size() - 1] == 0x0a || mbuf.data()[mbuf.size() - 1] == 0x0d)
mbuf.size(mbuf.size() - 1);
else
break;
}
mbuf.append((ex_u8*)"\x00", 1);
m_sid = (char*)mbuf.data();
return _do_connect_server();
}
else {
m_is_putty_mode = true;
}
}
MemBuffer mbuf_msg;
mbuf_msg.reserve(128);
MemStream ms_msg(mbuf_msg);
@ -239,6 +268,7 @@ sess_state TelnetSession::_do_negotiation_with_client(TelnetConn* conn) {
if (mbuf_sub.size() > 0)
{
// 已经得到一个sub negotiation在处理新的数据前先处理掉旧的
EXLOG_BIN(mbuf_sub.data(), mbuf_sub.size(), "-=-=-=-=-=");
ms_sub.reset();
}
@ -317,26 +347,36 @@ sess_state TelnetSession::_do_negotiation_with_client(TelnetConn* conn) {
if (mbuf_sub.size() == 5)
{
if (0 == memcmp(mbuf_sub.data(), "\x1f\x00\x50\x00\x18", 5))
{
m_is_putty_mode = true;
// ts_u8 _d[] = {
// 0xff, 0xfe, 0x20, 0xff, 0xfd, 0x18, 0xff, 0xfa, 0x27, 0x01, 0xff, 0xf0, 0xff, 0xfa, 0x27, 0x01,
// 0x03, 0x53, 0x46, 0x55, 0x54, 0x4c, 0x4e, 0x54, 0x56, 0x45, 0x52, 0x03, 0x53, 0x46, 0x55, 0x54,
// 0x4c, 0x4e, 0x54, 0x4d, 0x4f, 0x44, 0x45, 0xff, 0xf0, 0xff, 0xfd, 0x03
// };
ex_u8 _d[] = {
0xff, 0xfa, 0x27, 0x01,
0x03, 0x53, 0x46, 0x55, 0x54, 0x4c, 0x4e, 0x54, 0x56, 0x45, 0x52, 0x03, 0x53, 0x46, 0x55, 0x54,
0x4c, 0x4e, 0x54, 0x4d, 0x4f, 0x44, 0x45, 0xff, 0xf0
};
m_conn_client->send((ex_u8*)_d, sizeof(_d));
// 客户端窗口大小
if (0x1f == mbuf_sub.data()[0]) {
ms_sub.rewind();
ms_sub.skip(1);
m_win_width = ms_sub.get_u16_be();
m_win_height = ms_sub.get_u16_be();
ms_resp.reset();
ms_resp.put_u8(TELNET_IAC);
ms_resp.put_u8(TELNET_SB);
ms_resp.put_bin(mbuf_sub.data(), 5);
ms_resp.put_u8(TELNET_IAC);
ms_resp.put_u8(TELNET_SE);
conn->send(mbuf_resp.data(), mbuf_resp.size());
}
// 发送下列指令putty才会将登陆用户名发过来也就是SID
ex_u8 _d[] = {
0xff, 0xfa, 0x27, 0x01,
0x03, 0x53, 0x46, 0x55, 0x54, 0x4c, 0x4e, 0x54, 0x56, 0x45, 0x52, 0x03, 0x53, 0x46, 0x55, 0x54,
0x4c, 0x4e, 0x54, 0x4d, 0x4f, 0x44, 0x45, 0xff, 0xf0
};
m_conn_client->send((ex_u8*)_d, sizeof(_d));
return s_negotiation_with_client;
}
else if (mbuf_sub.size() > 8)
if (mbuf_sub.size() > 8)
{
// 可能含有putty的登录用户名信息就是SID啦
if (0 == memcmp(mbuf_sub.data(), "\x27\x00\x00\x55\x53\x45\x52\x01", 8)) // '..USER.
if (0 == memcmp(mbuf_sub.data(), "\x27\x00\x00\x55\x53\x45\x52\x01", 8)) // '...USER.
{
mbuf_sub.pop(8);
for (; mbuf_sub.size() > 0;)
@ -352,62 +392,49 @@ sess_state TelnetSession::_do_negotiation_with_client(TelnetConn* conn) {
}
}
if (mbuf_msg.size() > 0)
{
if (0 == memcmp(mbuf_msg.data(), "session:", 8))
{
mbuf_msg.pop(8);
for (; mbuf_msg.size() > 0;)
{
if (mbuf_msg.data()[mbuf_msg.size() - 1] == 0x0a || mbuf_msg.data()[mbuf_msg.size() - 1] == 0x0d)
mbuf_msg.size(mbuf_msg.size() - 1);
else
break;
}
mbuf_msg.append((ex_u8*)"\x00", 1);
m_sid = (char*)mbuf_msg.data();
}
}
if (m_sid.length() > 0)
{
EXLOGW("[telnet] session-id: %s\n", m_sid.c_str());
m_conn_info = g_telnet_env.get_connect_info(m_sid.c_str());
if (NULL == m_conn_info) {
EXLOGE("[telnet] no such session: %s\n", m_sid.c_str());
return _do_close(TP_SESS_STAT_ERR_SESSION);
}
else {
m_conn_ip = m_conn_info->conn_ip;
m_conn_port = m_conn_info->conn_port;
m_acc_name = m_conn_info->acc_username;
m_acc_secret = m_conn_info->acc_secret;
m_username_prompt = m_conn_info->username_prompt;
m_password_prompt = m_conn_info->password_prompt;
if (m_conn_info->protocol_type != TP_PROTOCOL_TYPE_TELNET) {
EXLOGE("[telnet] session '%s' is not for TELNET.\n", m_sid.c_str());
return _do_close(TP_SESS_STAT_ERR_SESSION);
}
}
// try to connect to real server.
m_conn_server->connect(m_conn_ip.c_str(), m_conn_port);
ex_astr szmsg = "Connect to ";
szmsg += m_conn_ip;
szmsg += "\r\n";
m_conn_client->send((ex_u8*)szmsg.c_str(), szmsg.length());
return s_noop;
return _do_connect_server();
}
return s_negotiation_with_client;
}
sess_state TelnetSession::_do_connect_server() {
EXLOGW("[telnet] session-id: [%s]\n", m_sid.c_str());
m_conn_info = g_telnet_env.get_connect_info(m_sid.c_str());
if (NULL == m_conn_info) {
EXLOGE("[telnet] no such session: %s\n", m_sid.c_str());
return _do_close(TP_SESS_STAT_ERR_SESSION);
}
else {
m_conn_ip = m_conn_info->conn_ip;
m_conn_port = m_conn_info->conn_port;
m_acc_name = m_conn_info->acc_username;
m_acc_secret = m_conn_info->acc_secret;
m_username_prompt = m_conn_info->username_prompt;
m_password_prompt = m_conn_info->password_prompt;
if (m_conn_info->protocol_type != TP_PROTOCOL_TYPE_TELNET) {
EXLOGE("[telnet] session '%s' is not for TELNET.\n", m_sid.c_str());
return _do_close(TP_SESS_STAT_ERR_SESSION);
}
}
// try to connect to real server.
m_conn_server->connect(m_conn_ip.c_str(), m_conn_port);
// ex_astr szmsg = "Connect to ";
// szmsg += m_conn_ip;
// szmsg += "\r\n";
// m_conn_client->send((ex_u8*)szmsg.c_str(), szmsg.length());
return s_noop;
}
sess_state TelnetSession::_do_server_connected() {
m_conn_client->data().empty();
m_conn_server->data().empty();
@ -433,11 +460,10 @@ sess_state TelnetSession::_do_relay(TelnetConn *conn) {
if (conn->is_server_side())
{
// 收到了客户端发来的数据
if (_this->m_is_putty_mode && !_this->m_is_putty_eat_username)
if (_this->m_is_putty_mode && !_this->m_username_sent)
{
if (_this->_eat_username(m_conn_client, m_conn_server))
if (_this->_putty_replace_username(m_conn_client, m_conn_server))
{
_this->m_is_putty_eat_username = true;
_this->m_username_sent = true;
is_processed = true;
}
@ -449,6 +475,11 @@ sess_state TelnetSession::_do_relay(TelnetConn *conn) {
return s_relay;
}
if (_this->_parse_win_size(m_conn_client)) {
if (m_record_started)
m_rec.record_win_size_change(m_win_width, m_win_height);
}
m_conn_server->send(m_conn_client->data().data(), m_conn_client->data().size());
m_conn_client->data().empty();
}
@ -478,6 +509,8 @@ sess_state TelnetSession::_do_relay(TelnetConn *conn) {
if (!_on_session_begin()) {
return _do_close(TP_SESS_STAT_ERR_INTERNAL);
}
m_rec.record_win_size_startup(m_win_width, m_win_height);
}
}
@ -578,7 +611,7 @@ bool TelnetSession::_parse_find_and_send(TelnetConn* conn_recv, TelnetConn* conn
return false;
}
bool TelnetSession::_eat_username(TelnetConn* conn_recv, TelnetConn* conn_remote)
bool TelnetSession::_putty_replace_username(TelnetConn* conn_recv, TelnetConn* conn_remote)
{
bool replaced = false;
@ -640,8 +673,7 @@ bool TelnetSession::_eat_username(TelnetConn* conn_recv, TelnetConn* conn_remote
continue;
}
// 到这里就找到了客户端发来的用户名,我们将其抛弃掉,发给服务端的是一个无用户名的包,这样
// 服务端就会提示输入用户名login:),后续检测到服务端的提示就会发送用户名了。
// 到这里就找到了客户端发来的用户名,我们将其替换为真实的远程账号。
ms_msg.put_u8(TELNET_IAC);
ms_msg.put_u8(TELNET_SB);
@ -676,3 +708,60 @@ bool TelnetSession::_eat_username(TelnetConn* conn_recv, TelnetConn* conn_remote
return false;
}
bool TelnetSession::_parse_win_size(TelnetConn* conn) {
if (conn->data().size() < 9)
return false;
if (conn->data().data()[0] != TELNET_IAC)
return false;
bool is_sub = false;
MemStream s(conn->data());
for (; s.left() > 0;)
{
if (s.get_u8() == TELNET_IAC)
{
if (s.left() < 2)
return false;
if (s.get_u8() == TELNET_SB)
{
size_t _begin = s.offset();
size_t _end = 0;
// SUB NEGOTIATION变长数据以 TELNET_IAC+TELNET_SE (FF F0) 结束
bool have_SE = false;
ex_u8 ch_sub = 0;
for (; s.left() > 0;)
{
_end = s.offset();
if (s.get_u8() == TELNET_IAC)
{
if (s.left() > 0)
{
if (s.get_u8() == TELNET_SE)
{
have_SE = true;
break;
}
else
return false;
}
}
}
if (!have_SE)
return false;
size_t len = _end - _begin;
if (len == 5 && 0x1F == conn->data().data()[_begin]) {
s.seek(_begin + 1);
m_win_width = s.get_u16_be();
m_win_height = s.get_u16_be();
return true;
}
}
}
}
return false;
}

View File

@ -65,13 +65,15 @@ protected:
private:
sess_state _do_client_connect(TelnetConn* conn);
sess_state _do_negotiation_with_client(TelnetConn* conn);
sess_state _do_connect_server();
sess_state _do_server_connected();
sess_state _do_relay(TelnetConn* conn);
sess_state _do_close(int err_code);
sess_state _do_check_closing();
bool _parse_find_and_send(TelnetConn* conn_recv, TelnetConn* conn_remote, const char* find, const char* send);
bool _eat_username(TelnetConn* conn_recv, TelnetConn* conn_remote);
bool _putty_replace_username(TelnetConn* conn_recv, TelnetConn* conn_remote);
bool _parse_win_size(TelnetConn* conn);
private:
int m_state;
@ -80,10 +82,13 @@ private:
bool m_record_started;
int m_db_id;
bool m_first_client_pkg;
bool m_is_relay; // 是否进入relay模式了只有进入relay模式才会有录像存在
bool m_is_closed;
TppTelnetRec m_rec;
int m_win_width;
int m_win_height;
TelnetProxy* m_proxy;
TelnetConn* m_conn_client; // 与真正客户端通讯的连接(自身作为服务端)
@ -103,11 +108,10 @@ private:
ex_astr m_client_addr;
bool m_is_putty_mode;
bool m_is_putty_eat_username;
//bool m_is_putty_eat_username;
bool m_username_sent;
bool m_password_sent;
};
#endif // __TELNET_SESSION_H__