From e920bf18959cebfa489c0990008decf486face6d Mon Sep 17 00:00:00 2001 From: Apex Liu Date: Fri, 6 Apr 2018 06:56:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=AF=B9telnet=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E7=9A=84=E6=94=AF=E6=8C=81=EF=BC=8C=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E6=AD=A3=E5=B8=B8=E8=BF=9E=E6=8E=A5=E8=BF=9C=E7=A8=8B=E4=B8=BB?= =?UTF-8?q?=E6=9C=BA=EF=BC=8C=E8=BF=98=E5=B7=AE=E5=BD=95=E5=83=8F=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=B2=A1=E6=9C=89=E8=AE=B0=E5=BD=95=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/tp_core/protocol/telnet/CMakeLists.txt | 42 + server/tp_core/protocol/telnet/dllmain.cpp | 27 + server/tp_core/protocol/telnet/stdafx.cpp | 16 + server/tp_core/protocol/telnet/stdafx.h | 16 + server/tp_core/protocol/telnet/targetver.h | 11 + .../tp_core/protocol/telnet/telnet_conn.cpp | 312 ++++++++ server/tp_core/protocol/telnet/telnet_conn.h | 162 ++++ .../tp_core/protocol/telnet/telnet_proxy.cpp | 210 +++++ server/tp_core/protocol/telnet/telnet_proxy.h | 55 ++ .../protocol/telnet/telnet_recorder.cpp | 191 +++++ .../tp_core/protocol/telnet/telnet_recorder.h | 53 ++ .../protocol/telnet/telnet_session.cpp | 742 ++++++++++++++++++ .../tp_core/protocol/telnet/telnet_session.h | 128 +++ server/tp_core/protocol/telnet/tpp_env.cpp | 33 + server/tp_core/protocol/telnet/tpp_env.h | 22 + server/tp_core/protocol/telnet/tptelnet.cpp | 32 + .../protocol/telnet/tptelnet.vs2015.sln | 22 + .../protocol/telnet/tptelnet.vs2015.vcxproj | 234 ++++++ .../telnet/tptelnet.vs2015.vcxproj.filters | 422 ++++++++++ .../www/teleport/static/js/asset/host-list.js | 50 +- .../www/teleport/static/js/ops/remote-list.js | 4 +- server/www/teleport/view/asset/host-list.mako | 2 +- .../webroot/app/controller/account.py | 2 + .../teleport/webroot/app/controller/ops.py | 13 +- .../www/teleport/webroot/app/model/account.py | 16 +- 25 files changed, 2797 insertions(+), 20 deletions(-) create mode 100644 server/tp_core/protocol/telnet/CMakeLists.txt create mode 100644 server/tp_core/protocol/telnet/dllmain.cpp create mode 100644 server/tp_core/protocol/telnet/stdafx.cpp create mode 100644 server/tp_core/protocol/telnet/stdafx.h create mode 100644 server/tp_core/protocol/telnet/targetver.h create mode 100644 server/tp_core/protocol/telnet/telnet_conn.cpp create mode 100644 server/tp_core/protocol/telnet/telnet_conn.h create mode 100644 server/tp_core/protocol/telnet/telnet_proxy.cpp create mode 100644 server/tp_core/protocol/telnet/telnet_proxy.h create mode 100644 server/tp_core/protocol/telnet/telnet_recorder.cpp create mode 100644 server/tp_core/protocol/telnet/telnet_recorder.h create mode 100644 server/tp_core/protocol/telnet/telnet_session.cpp create mode 100644 server/tp_core/protocol/telnet/telnet_session.h create mode 100644 server/tp_core/protocol/telnet/tpp_env.cpp create mode 100644 server/tp_core/protocol/telnet/tpp_env.h create mode 100644 server/tp_core/protocol/telnet/tptelnet.cpp create mode 100644 server/tp_core/protocol/telnet/tptelnet.vs2015.sln create mode 100644 server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj create mode 100644 server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj.filters diff --git a/server/tp_core/protocol/telnet/CMakeLists.txt b/server/tp_core/protocol/telnet/CMakeLists.txt new file mode 100644 index 0000000..3f7b39b --- /dev/null +++ b/server/tp_core/protocol/telnet/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.5) +project(tptelnet) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${Project_SOURCE_DIR}/../out/server/x64/bin") + + +aux_source_directory(. DIR_RDP_SRCS) +aux_source_directory(../../common DIR_RDP_SRCS) +aux_source_directory(../../../../common/libex/src DIR_RDP_SRCS) + +list(REMOVE_ITEM DIR_RDP_SRCS "./dllmain.cpp") +list(REMOVE_ITEM DIR_RDP_SRCS "./stdafx.cpp") + + +include_directories( + ../../../../common/libex/include + ../../../../common/teleport +) + +IF (CMAKE_SYSTEM_NAME MATCHES "Linux") + include_directories( + ../../../../external/linux/release/include + ) + link_directories(../../../../external/linux/release/lib) +ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin") + include_directories( + ../../../../external/macos/release/include + ) + link_directories(../../../../external/macos/release/lib) +ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux") + + +add_library(tptelnet SHARED ${DIR_RDP_SRCS}) + +IF (CMAKE_SYSTEM_NAME MATCHES "Linux") + target_link_libraries(tptelnet uv mbedx509 mbedtls mbedcrypto dl pthread rt util) +ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin") + target_link_libraries(tptelnet uv mbedx509 mbedtls mbedcrypto dl pthread util) +ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux") + diff --git a/server/tp_core/protocol/telnet/dllmain.cpp b/server/tp_core/protocol/telnet/dllmain.cpp new file mode 100644 index 0000000..f355ee2 --- /dev/null +++ b/server/tp_core/protocol/telnet/dllmain.cpp @@ -0,0 +1,27 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +#ifdef _DEBUG +#include +#endif + +#pragma comment(lib, "userenv.lib") // for GetUserProfileDirectory() +#pragma comment(lib, "psapi.lib") // for GetProcessMemoryInfo() +#pragma comment(lib, "iphlpapi.lib") // for GetAdaptersAddresses() + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/server/tp_core/protocol/telnet/stdafx.cpp b/server/tp_core/protocol/telnet/stdafx.cpp new file mode 100644 index 0000000..92e4620 --- /dev/null +++ b/server/tp_core/protocol/telnet/stdafx.cpp @@ -0,0 +1,16 @@ +// stdafx.cpp : source file that includes just the standard includes +// tpssh.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file + +#include + +#ifdef EX_OS_WIN32 +//# pragma comment(lib, "libeay32.lib") +# pragma comment(lib, "ws2_32.lib") +#endif + diff --git a/server/tp_core/protocol/telnet/stdafx.h b/server/tp_core/protocol/telnet/stdafx.h new file mode 100644 index 0000000..677e68a --- /dev/null +++ b/server/tp_core/protocol/telnet/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/server/tp_core/protocol/telnet/targetver.h b/server/tp_core/protocol/telnet/targetver.h new file mode 100644 index 0000000..b310de7 --- /dev/null +++ b/server/tp_core/protocol/telnet/targetver.h @@ -0,0 +1,11 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include +#define _WIN32_WINNT 0x0600 // WinVista + +#include diff --git a/server/tp_core/protocol/telnet/telnet_conn.cpp b/server/tp_core/protocol/telnet/telnet_conn.cpp new file mode 100644 index 0000000..0704129 --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_conn.cpp @@ -0,0 +1,312 @@ +#include "telnet_conn.h" +#include "telnet_session.h" +#include "../../common/ts_memstream.h" +#include "../../common/ts_const.h" +#include + +TelnetConn::TelnetConn(TelnetSession *sess, bool is_server_side) : m_session(sess), m_is_server(is_server_side) { + if (is_server_side) { + m_name = "cli<->tp"; + m_state = TELNET_CONN_STATE_CONNECTED; + } + else { + m_name = "tp<->srv"; + m_state = TELNET_CONN_STATE_FREE; + } + + m_is_recving = false; + + m_timer_running = false; + + uv_tcp_init(sess->get_loop(), &m_handle); + m_handle.data = this; +} + +TelnetConn::~TelnetConn() { +} + +bool TelnetConn::start_recv() { + m_is_recving = true; + int err = uv_read_start((uv_stream_t *)&m_handle, _on_alloc, _on_recv); + if (err != 0) { + EXLOGE("[telnet] [%s] can not start to read.\n", m_name); + m_session->close(TP_SESS_STAT_ERR_IO); + return false; + } + + m_is_recving = true; + return true; +} + +void TelnetConn::close() { + if (m_state == TELNET_CONN_STATE_FREE || m_state == TELNET_CONN_STATE_CLOSING) + return; + + if (m_timer_running) { + m_timer_running = false; + uv_timer_stop(&m_timer_connect_timeout); + + EXLOGW("[telnet] [%s] try to close while it connecting.\n", m_name); + m_state = TELNET_CONN_STATE_CLOSING; + uv_close(handle(), NULL); + + return; + } + + if (m_is_recving) { + m_is_recving = false; + uv_read_stop((uv_stream_t*)&m_handle); + } + +// int uverr = 0; +// uv_shutdown_t *sreq = (uv_shutdown_t *)calloc(1, sizeof(uv_shutdown_t)); +// sreq->data = this; +// if ((uverr = uv_shutdown(sreq, stream_handle(), _uv_on_shutdown)) != 0) { +// EXLOGW("[telnet] [%s] error when shutdown connection. %s\n", m_name, uv_strerror(uverr)); +// free(sreq); +// +// m_state = TELNET_CONN_STATE_FREE; +// +// m_session->on_conn_close(); +// } +// else { +// m_state = RDP_CONN_STATE_CLOSING; +// } + + uv_close(handle() , _uv_on_closed); + +} + + +// void TelnetConn::_uv_on_shutdown(uv_shutdown_t *req, int status) { +// TelnetConn *_this = (TelnetConn *)req->data; +// //EXLOGD("[telnet] [%s] .. _uv_on_shutdown, status=%d\n", _this->m_name, status); +// uv_close((uv_handle_t *)req->handle, _uv_on_closed); +// free(req); +// } + +void TelnetConn::_uv_on_closed(uv_handle_t *handle) { + TelnetConn *_this = (TelnetConn *)handle->data; + //EXLOGD("[telnet] [%s] .. _uv_on_closed\n", _this->m_name); + _this->m_state = TELNET_CONN_STATE_FREE; + _this->m_session->on_conn_close(); +} + +void TelnetConn::_on_alloc(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + TelnetConn *_this = (TelnetConn *) handle->data; + buf->base = (char *) calloc(1, suggested_size); + buf->len = suggested_size; +} + +void TelnetConn::_on_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { + TelnetConn *_this = (TelnetConn *) handle->data; + + //ExThreadSmartLock locker(_this->m_locker_recv); + + if (nread == 0) { + free(buf->base); + //_this->m_session->do_next(_this); + return; + } + else if (nread < 0) { + free(buf->base); + + if (nread == -4077) + EXLOGD("[telnet] [%s] [recv] disconnected.\n", _this->m_name); + else if (nread == -104) + EXLOGD("[telnet] [%s] [recv] connection reset by peer.\n", _this->m_name); + else + EXLOGD("[telnet] [%s] [recv] nread=%d.\n", _this->m_name, nread); + + _this->m_session->close(TP_SESS_STAT_END); + return; + } + 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); +// #endif + } + + EXLOG_BIN((ex_u8*)buf->base, nread, "--READ-- %s", _this->m_name); + + _this->m_buf_data.append((ex_u8 *) buf->base, nread); + free(buf->base); + + _this->m_session->do_next(_this); +} + +bool TelnetConn::send(MemBuffer &mbuf) { + return _raw_send(mbuf.data(), mbuf.size()); +} + +// bool TelnetConn::send(TelnetPkgBase &pkg) { +// MemBuffer mbuf; +// MemStream s(mbuf); +// if (TPE_OK != pkg.build(s)) { +// EXLOGE("[telnet] when send, can not build package to binary.\n"); +// return false; +// } +// +// return _raw_send(mbuf.data(), mbuf.size()); +// } + +bool TelnetConn::send(const ex_u8 *data, size_t size) { + return _raw_send(data, 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); +// #endif + + return raw_send(data, size); +} + +bool TelnetConn::raw_send(const ex_u8 *data, size_t size) { + uv_write_t *w = (uv_write_t *) calloc(1, sizeof(uv_write_t)); + + ex_u8 *_data = (ex_u8 *) calloc(1, size); + if (NULL == _data) { + free(w); + EXLOGE("[telnet] alloc buffer %dB failed.\n", size); + return false; + } + memcpy(_data, data, size); + + uv_buf_t *_buf = (uv_buf_t *) calloc(1, sizeof(uv_buf_t)); + _buf->base = (char *) _data; + _buf->len = size; + + w->data = _buf; + if (0 == uv_write(w, (uv_stream_t *) &m_handle, _buf, 1, _on_send_done)) + return true; + else { + EXLOGE("[telnet] [%s] raw_send() failed.\n", m_name); + return false; + } +} + +void TelnetConn::_on_send_done(uv_write_t *req, int status) { + uv_buf_t *_buf = (uv_buf_t *) req->data; + free(_buf->base); + free(_buf); + free(req); + + if (status == UV_ECANCELED) { + EXLOGE("[telnet] _on_send_done() got UV_ECANCELED.\n"); + return; + } +} + +void TelnetConn::connect(const char *server_ip, ex_u16 server_port) { + m_server_ip = server_ip; + m_server_port = server_port; + + if (m_state == TELNET_CONN_STATE_CONNECTED) { + // 当前已经连接上了服务器了,断开重连 + EXLOGV("[telnet] [%s] [%s] try to disconnect. %s:%d\n", m_name, m_session->client_addr(), server_ip, server_port); + m_state = TELNET_CONN_STATE_CLOSING; + uv_close((uv_handle_t *) &m_handle, _uv_on_reconnect); + return; + } + else { + EXLOGV("[telnet] [%s] [%s] try to connect to real TELNET server at %s:%d\n", m_name, m_session->client_addr(), server_ip, server_port); + } + + struct sockaddr_in addr; + uv_ip4_addr(server_ip, server_port, &addr); + + uv_connect_t *conn_req = (uv_connect_t *) calloc(1, sizeof(uv_connect_t)); + conn_req->data = this; + + // 设置一个超时回调,如果超时发生时连接尚未完成,就报错 + uv_timer_init(m_session->get_loop(), &m_timer_connect_timeout); + m_timer_connect_timeout.data = this; + +#ifdef EX_DEBUG + uv_timer_start(&m_timer_connect_timeout, _uv_on_connect_timeout, 5000, 0); +#else + uv_timer_start(&m_timer_connect_timeout, _uv_on_connect_timeout, 10000, 0); +#endif + + m_timer_running = true; + + //m_is_connecting = true; + m_state = TELNET_CONN_STATE_CONNECTING; + int err = 0; + if ((err = uv_tcp_connect(conn_req, &m_handle, (const struct sockaddr *) &addr, _uv_on_connected)) != 0) { + free(conn_req); + EXLOGE("[telnet] [%s] can not connect to server: %s\n", m_name, uv_strerror(err)); + + m_timer_running = false; + uv_timer_stop(&m_timer_connect_timeout); + uv_close((uv_handle_t*)&m_timer_connect_timeout, _uv_on_timer_connect_timeout_closed); + + m_state = TELNET_CONN_STATE_FREE; + m_session->close(TP_SESS_STAT_ERR_CONNECT); + } +} + +void TelnetConn::_uv_on_connect_timeout(uv_timer_t *timer) +{ + TelnetConn *_this = (TelnetConn *)timer->data; + //EXLOGD("[telnet] [%s] .. _uv_on_connect_timeout.\n", _this->m_name); + + if (_this->m_timer_running) { + _this->m_timer_running = false; + uv_timer_stop(&_this->m_timer_connect_timeout); + uv_close((uv_handle_t*)&_this->m_timer_connect_timeout, _this->_uv_on_timer_connect_timeout_closed); + } + + // 如果在连接成功之前就超时了,则关闭连接 + EXLOGE("[telnet] [%s] timeout when connect to real TELNET server, cancel connection.\n", _this->m_name); + _this->m_state = TELNET_CONN_STATE_CLOSING; + uv_close(_this->handle(), _uv_on_closed); +} + +void TelnetConn::_uv_on_reconnect(uv_handle_t *handle) { + TelnetConn *_this = (TelnetConn *)handle->data; + //EXLOGD("[telnet] [%s] .. _uv_on_reconnect.\n", _this->m_name); + _this->m_state = TELNET_CONN_STATE_FREE; + + uv_tcp_init(_this->m_session->get_loop(), &_this->m_handle); + _this->m_handle.data = _this; + + _this->connect(_this->m_server_ip.c_str(), _this->m_server_port); +} + +void TelnetConn::_uv_on_connected(uv_connect_t *req, int status) { + TelnetConn *_this = (TelnetConn *)req->data; + free(req); + //EXLOGD("[telnet] [%s] .. _uv_on_connected: status=%d.\n", _this->m_name, status); + + if (_this->m_timer_running) { + _this->m_timer_running = false; + uv_timer_stop(&_this->m_timer_connect_timeout); + uv_close((uv_handle_t*)&_this->m_timer_connect_timeout, _this->_uv_on_timer_connect_timeout_closed); + } + + if (status != 0) { + EXLOGE("[telnet] [%s] cannot connect to real TELNET server. %s\n", _this->m_name, uv_strerror(status)); + _this->m_state = TELNET_CONN_STATE_FREE; + _this->m_session->close(TP_SESS_STAT_ERR_CONNECT); + return; + } + + EXLOGW("[telnet] [%s] real TELNET server connected.\n", _this->m_session->client_addr()); + _this->m_state = TELNET_CONN_STATE_CONNECTED; + + if (!_this->start_recv()) { + _this->m_session->close(TP_SESS_STAT_ERR_IO); + return; + } + + _this->m_session->do_next(_this, s_server_connected); +} + +//static +void TelnetConn::_uv_on_timer_connect_timeout_closed(uv_handle_t *handle) { + +} diff --git a/server/tp_core/protocol/telnet/telnet_conn.h b/server/tp_core/protocol/telnet/telnet_conn.h new file mode 100644 index 0000000..ed3679b --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_conn.h @@ -0,0 +1,162 @@ +#ifndef __TELNET_CONN_H__ +#define __TELNET_CONN_H__ + +#include + +// typedef time_t mbedtls_time_t; + +// #include +// #include +// #include +// #include + +// #include "telnet_package.h" +#include "../../common/ts_membuf.h" +#include "../../common/ts_memstream.h" +// #include "telnet_bulk.h" + + + +//#define LOG_DATA + +#define TELNET_CONN_STATE_FREE 0 // not connected yet or closed +#define TELNET_CONN_STATE_CONNECTING 1 // connecting +#define TELNET_CONN_STATE_CONNECTED 2 // connected. +#define TELNET_CONN_STATE_CLOSING 3 // closing. + + +class TelnetSession; + +class TelnetConn { +public: + TelnetConn(TelnetSession *sess, bool is_server_side); + ~TelnetConn(); + + TelnetSession *session() { return m_session; } + + // just for debug-info + const char *name() const { return m_name; } + + bool is_server_side() const { return m_is_server; } + ex_u8 state() const { return m_state; } + + uv_handle_t *handle() { return (uv_handle_t *) &m_handle; } + uv_tcp_t *tcp_handle() { return &m_handle; } + uv_stream_t *stream_handle() { return (uv_stream_t *) &m_handle; } + + MemBuffer &data() { return m_buf_data; } + + bool send(MemBuffer &mbuf); +// bool send(TelnetPkgBase &pkg); + bool send(const ex_u8 *data, size_t size); + bool raw_send(const ex_u8 *data, size_t size); + + // connect to real server, for proxy-client-side only. + void connect(const char *server_ip, ex_u16 server_port = 3389); + // try to close this connection. return current RDP_CONN_STATE_XXXX. + void close(); + bool start_recv(); + + + // 密钥相关 +// void gen_session_keys(ex_u8 *client_random, ex_u8 *server_random); +// void decrypt(ex_u8 *buf_data, size_t buf_size, bool update_counter); +// void encrypt(ex_u8 *buf_data, size_t buf_size, bool update_counter); +// ex_u32 get_dec_counter() { return m_decrypt_total; } +// ex_u32 get_enc_counter() { return m_encrypt_total; } +// void calc_mac(ex_u8 *buf_data, size_t buf_size, ex_u8 *signature, bool with_counter = false, ex_u32 counter = 0); + + // RDP-SSL 相关 +// bool ssl_prepare(); + +// mbedtls_ssl_context *ssl_context() { return &m_ssl_ctx; } + +// bool ssl_is_in_handshake() { return m_ssl_ctx.state != MBEDTLS_SSL_HANDSHAKE_OVER; } +// int ssl_do_handshake(); +// int ssl_do_read(); + +// bool ssl_send(MemBuffer &mbuf); +// bool ssl_send(TelnetPkgBase &pkg); +// bool ssl_send(const ex_u8 *data, size_t size); + +// MemBuffer &ssl_data() { return m_ssl_mbuf; } + +// static int on_ssl_read(void *ctx, ex_u8 *buf, size_t len); +// static int on_ssl_write(void *ctx, const ex_u8 *buf, size_t len); + + //RDP_BULK* get_bulk() { return m_bulk; } + +private: + static void _on_alloc(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); + static void _on_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf); + static void _on_send_done(uv_write_t *req, int status); + static void _uv_on_connect_timeout(uv_timer_t *timer); + static void _uv_on_connected(uv_connect_t *req, int status); + static void _uv_on_reconnect(uv_handle_t *handle); + static void _uv_on_shutdown(uv_shutdown_t *req, int status); + static void _uv_on_closed(uv_handle_t *handle); + static void _uv_on_timer_connect_timeout_closed(uv_handle_t *handle); + +// static void _sec_hash_48(ex_u8 *buf_out, ex_u8 *buf_in, ex_u8 *salt1, ex_u8 *salt2, ex_u8 salt); +// static void _sec_hash_16(ex_u8 *buf_out, ex_u8 *buf_in, ex_u8 *salt1, ex_u8 *salt2); +// void _sec_update(ex_u8 *init_key, ex_u8 *curr_key); + + bool _raw_send(const ex_u8 *data, size_t size); +// bool _ssl_send(const ex_u8 *data, size_t size); +// bool _ssl_prepare_as_server(); +// bool _ssl_prepare_as_client(); + +private: + TelnetSession *m_session; + bool m_is_server; + + // for debug-info. + const char *m_name; + + uv_tcp_t m_handle; + uv_timer_t m_timer_connect_timeout; + bool m_timer_running; // does m_timer_connect_timeout initialized and started. + + ex_u8 m_state; // RDP_CONN_STATE_XXXX + + // 作为client需要的数据(远程主机信息) + std::string m_server_ip; + ex_u16 m_server_port; + bool m_is_recving; // does this connection is receiving data? + + ExThreadLock m_locker_send; + ExThreadLock m_locker_recv; + MemBuffer m_buf_data; + + + // 会话密钥相关 +// ex_u8 m_rc4_key_len; +// ex_u8 m_mac_key[16]; +// ex_u8 m_init_enc_key[16]; +// ex_u8 m_init_dec_key[16]; +// ex_u8 m_curr_enc_key[16]; +// ex_u8 m_curr_dec_key[16]; +// mbedtls_arc4_context m_rc4_encrypt_key; +// mbedtls_arc4_context m_rc4_decrypt_key; + +// ex_u32 m_encrypt_count; +// ex_u32 m_decrypt_count; +// ex_u32 m_encrypt_total; +// ex_u32 m_decrypt_total; + + + // RDP-SSL相关 +// MemBuffer m_ssl_mbuf; // 存放接收到的数据(经过ssl解密)的缓冲区,等待处理。处理函数处理之后,应该将已经处理过的数据弹掉。 +// mbedtls_ssl_context m_ssl_ctx; +// mbedtls_x509_crt m_ssl_node_cert; +// mbedtls_pk_context m_ssl_node_key; +// mbedtls_ssl_config m_ssl_conf; +// mbedtls_entropy_context m_ssl_entropy; +// mbedtls_ctr_drbg_context m_ssl_ctr_drbg; +// mbedtls_x509_crt m_ssl_ca_cert; + + // 数据包解析相关 + //RDP_BULK* m_bulk; +}; + +#endif // __TELNET_CONN_H__ diff --git a/server/tp_core/protocol/telnet/telnet_proxy.cpp b/server/tp_core/protocol/telnet/telnet_proxy.cpp new file mode 100644 index 0000000..af066dc --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_proxy.cpp @@ -0,0 +1,210 @@ +#include "telnet_proxy.h" +#include "tpp_env.h" +#include + +TelnetProxy g_telnet_proxy; + +TelnetProxy::TelnetProxy() : ExThreadBase("telnet-proxy-thread") +{ + memset(&m_loop, 0, sizeof(uv_loop_t)); + m_timer_counter = 0; +} + +TelnetProxy::~TelnetProxy() +{ + if (m_sessions.size() > 0) + EXLOGE("[telnet] not all session stopped.\n"); +} + +bool TelnetProxy::init() +{ + if (0 != uv_loop_init(&m_loop)) + return false; + + if (0 != uv_async_init(&m_loop, &m_clean_session_handle, _on_clean_session_cb)) + return false; + m_clean_session_handle.data = this; + + m_host_ip = g_telnet_env.bind_ip; + m_host_port = g_telnet_env.bind_port; + + if (0 != uv_tcp_init(&m_loop, &m_handle)) + return false; + m_handle.data = this; + + return true; +} + +void TelnetProxy::timer() { + // 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; + + m_timer_counter = 0; + + ExThreadSmartLock locker(m_lock); + ts_telnet_sessions::iterator it = m_sessions.begin(); + for (; it != m_sessions.end(); ++it) + { + it->first->save_record(); + } +} + +void TelnetProxy::_thread_loop(void) +{ + struct sockaddr_in addr; + if (0 != uv_ip4_addr(m_host_ip.c_str(), m_host_port, &addr)) { + EXLOGE("[telnet] invalid ip/port for TELNET listener.\n"); + return; + } + + if (0 != uv_tcp_bind(&m_handle, (const struct sockaddr*) &addr, 0)) { + EXLOGE("[telnet] can not bind %s:%d.\n", m_host_ip.c_str(), m_host_port); + return; + } + + // 开始监听,有客户端连接到来时,会回调 _on_client_connect() + if (0 != uv_listen((uv_stream_t*)&m_handle, 8, _on_client_connect)) { + EXLOGE("[telnet] can not listen on %s:%d.\n", m_host_ip.c_str(), m_host_port); + return; + } + + EXLOGI("[telnet] TeleportServer-TELNET ready on %s:%d\n", m_host_ip.c_str(), m_host_port); + + int err = 0; + if ((err = uv_run(&m_loop, UV_RUN_DEFAULT)) != 0) { + EXLOGE("[telnet] main-loop end. %s\n", uv_strerror(err)); + } + + // 注意,如果在 uv_loop_close() 内部崩溃,可能某个uv的handle未关闭。 + uv_loop_close(&m_loop); + + EXLOGV("[telnet] main-loop end.\n"); +} + +void TelnetProxy::_set_stop_flag(void) { + m_stop_flag = true; + + if (m_is_running) { + uv_close((uv_handle_t*)&m_handle, _on_listener_closed); + } +} + +// static +void TelnetProxy::_on_listener_closed(uv_handle_t* handle) +{ + TelnetProxy* _this = (TelnetProxy*)handle->data; + EXLOGV("[telnet] listener close.\n"); + + _this->_close_all_sessions(); +} + +void TelnetProxy::clean_session() { + uv_async_send(&m_clean_session_handle); +} + +void TelnetProxy::_close_all_sessions(void) +{ + ExThreadSmartLock locker(m_lock); + + if (m_sessions.size() == 0) { + _close_clean_session_handle(); + return; + } + + ts_telnet_sessions::iterator it = m_sessions.begin(); + for (; it != m_sessions.end(); ++it) + { + it->first->close(TP_SESS_STAT_ERR_RESET); + } +} + +// static +void TelnetProxy::_on_clean_session_cb(uv_async_t* handle) +{ + TelnetProxy* _this = (TelnetProxy*)handle->data; + + // check closed session + ExThreadSmartLock locker(_this->m_lock); + + ts_telnet_sessions::iterator it = _this->m_sessions.begin(); + for (; it != _this->m_sessions.end(); ) + { + if (it->first->is_closed()) { + delete it->first; + _this->m_sessions.erase(it++); + EXLOGD("[telnet] - removed one session.\n"); + } + else { + it++; + } + } + + if (_this->m_stop_flag && _this->m_sessions.size() == 0) { + _this->_close_clean_session_handle(); + } +} + +//static +void TelnetProxy::_on_clean_session_handle_closed(uv_handle_t *handle) { +} + + +// static +void TelnetProxy::_on_client_connect(uv_stream_t* server, int status) +{ + if (0 != status) + return; + + TelnetProxy* _this = (TelnetProxy*)server->data; + _this->_on_accept(server); +} + +bool TelnetProxy::_on_accept(uv_stream_t* server) +{ + TelnetSession* sess = new TelnetSession(this); + + if (0 != uv_accept(server, sess->client()->stream_handle())) + { + EXLOGE("[telnet] socket accept failed.\n"); + delete sess; + return false; + } + + if (m_stop_flag) + { + delete sess; + return false; + } + + // 获取客户端IP地址和端口号 + struct sockaddr sock_client; + int namelen = sizeof(sock_client); + if (0 == uv_tcp_getpeername(sess->client()->tcp_handle(), &sock_client, &namelen)) + { + sockaddr_in* addrin = (sockaddr_in*)&sock_client; + char ip[17] = { 0 }; + if (0 == uv_ip4_name(addrin, ip, sizeof(ip))) + { + char client_addr[64] = { 0 }; + snprintf(client_addr, 64, "%s:%d", ip, addrin->sin_port); + sess->client_addr(client_addr); + } + } + + EXLOGV("\n=================== NEW TELNET CLIENT [%s] ============\n", sess->client_addr()); + + { + ExThreadSmartLock locker(m_lock); + m_sessions.insert(std::make_pair(sess, 0)); + } + + sess->client()->start_recv(); + + return true; +} + +void TelnetProxy::_close_clean_session_handle() { + uv_close((uv_handle_t*)&m_clean_session_handle, _on_clean_session_handle_closed); +} diff --git a/server/tp_core/protocol/telnet/telnet_proxy.h b/server/tp_core/protocol/telnet/telnet_proxy.h new file mode 100644 index 0000000..1538082 --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_proxy.h @@ -0,0 +1,55 @@ +#ifndef __TELNET_PROXY_H__ +#define __TELNET_PROXY_H__ + +#include +#include + +#include "telnet_session.h" + +typedef std::map ts_telnet_sessions; + +class TelnetProxy : public ExThreadBase +{ +public: + TelnetProxy(); + ~TelnetProxy(); + + bool init(); + void timer(); + uv_loop_t* get_loop() { return &m_loop; } + + void clean_session(); + +protected: + void _thread_loop(); + void _set_stop_flag(); + void _close_all_sessions(); + void _close_clean_session_handle(); + +private: + static void _on_client_connect(uv_stream_t* server, int status); + static void _on_listener_closed(uv_handle_t* handle); + static void _on_clean_session_cb(uv_async_t* handle); + static void _on_clean_session_handle_closed(uv_handle_t *handle); + + bool _on_accept(uv_stream_t* server); + +private: + bool m_stop_flag; + int m_timer_counter; + + uv_loop_t m_loop; + uv_tcp_t m_handle; + uv_async_t m_clean_session_handle; + + ExThreadLock m_lock; + + ex_astr m_host_ip; + int m_host_port; + + ts_telnet_sessions m_sessions; +}; + +extern TelnetProxy g_telnet_proxy; + +#endif // __TELNET_PROXY_H__ diff --git a/server/tp_core/protocol/telnet/telnet_recorder.cpp b/server/tp_core/protocol/telnet/telnet_recorder.cpp new file mode 100644 index 0000000..d5941a1 --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_recorder.cpp @@ -0,0 +1,191 @@ +#include "telnet_recorder.h" + +static ex_u8 TPP_RECORD_MAGIC[4] = { 'T', 'P', 'P', 'R' }; + +TppTelnetRec::TppTelnetRec() +{ + m_cmd_cache.reserve(MAX_SIZE_PER_FILE); + + memset(&m_head, 0, sizeof(TS_RECORD_HEADER)); + memcpy((ex_u8*)(&m_head.info.magic), TPP_RECORD_MAGIC, sizeof(ex_u32)); + m_head.info.ver = 0x03; + m_header_changed = false; + m_save_full_header = false; + + m_file_info = NULL; + m_file_data = NULL; + m_file_cmd = NULL; +} + +TppTelnetRec::~TppTelnetRec() +{ + end(); +} + +bool TppTelnetRec::_on_begin(const TPP_CONNECT_INFO* info) +{ + if (NULL == info) + return false; + m_head.basic.timestamp = (ex_u64)time(NULL); + m_head.basic.protocol_type = (ex_u16)info->protocol_type; + m_head.basic.protocol_sub_type = (ex_u16)info->protocol_sub_type; + m_head.basic.conn_port = (ex_u16)info->conn_port; + + memcpy(m_head.basic.acc_username, info->acc_username, strlen(info->acc_username) >= 63 ? 63 : strlen(info->acc_username)); + memcpy(m_head.basic.user_username, info->user_username, strlen(info->user_username) >= 63 ? 63 : strlen(info->user_username)); + memcpy(m_head.basic.host_ip, info->host_ip, strlen(info->host_ip) >= 39 ? 39 : strlen(info->host_ip)); + memcpy(m_head.basic.conn_ip, info->conn_ip, strlen(info->conn_ip) >= 39 ? 39 : strlen(info->conn_ip)); + memcpy(m_head.basic.client_ip, info->client_ip, strlen(info->client_ip) >= 39 ? 39 : strlen(info->client_ip)); + + return true; +} + +bool TppTelnetRec::_on_end() +{ + // 如果还有剩下未写入的数据,写入文件中。 + save_record(); + + if (m_file_info != NULL) + fclose(m_file_info); + if (m_file_data != NULL) + fclose(m_file_data); + if (m_file_cmd != NULL) + fclose(m_file_cmd); + + return true; +} + +void TppTelnetRec::save_record() { + _save_to_data_file(); + _save_to_cmd_file(); +} + +// void TppTelnetRec::record_time_begin(void) // 指定从此时开始计时,之前收到的包会计时为0,这样播放时会快进到此处。 +// { +// m_start_time = ex_get_tick_count(); +// // m_head.timestamp = time(NULL); +// m_head.basic.timestamp = (ex_u64)time(NULL); +// m_save_full_header = true; +// m_header_changed = true; +// } + +void TppTelnetRec::record(ex_u8 type, const ex_u8* data, size_t size) +{ + if (data == NULL || 0 == size) + return; + + if (sizeof(TS_RECORD_PKG) + size + m_cache.size() > MAX_SIZE_PER_FILE) + _save_to_data_file(); + + TS_RECORD_PKG pkg = { 0 }; + pkg.type = type; + pkg.size = (ex_u32)size; + + if (m_start_time > 0) + { + pkg.time_ms = (ex_u32)(ex_get_tick_count() - m_start_time); + m_head.info.time_ms = pkg.time_ms; + } + + m_cache.append((ex_u8*)&pkg, sizeof(TS_RECORD_PKG)); + m_cache.append(data, size); + + m_head.info.packages++; + 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; + //_save_to_info_file(); +} + +bool TppTelnetRec::_save_to_info_file() { + if (!m_header_changed) + return true; + + if (m_file_info == NULL) { + ex_wstr fname = m_base_path; + ex_path_join(fname, false, m_base_fname.c_str(), NULL); + fname += L".tpr"; + + m_file_info = ex_fopen(fname, L"wb"); + if (NULL == m_file_info) + { + EXLOGE("[ssh] can not open record info-file for write.\n"); + return false; + } + + // first time to save header, write whole header. + m_save_full_header = true; + } + + fseek(m_file_info, 0L, SEEK_SET); + if (m_save_full_header) { + fwrite(&m_head, ts_record_header_size, 1, m_file_info); + fflush(m_file_info); + m_save_full_header = false; + } + else { + fwrite(&m_head.info, ts_record_header_info_size, 1, m_file_info); + fflush(m_file_info); + } + + return true; +} + +bool TppTelnetRec::_save_to_data_file() +{ + if (m_cache.size() == 0) + return true; + + if (m_file_data == NULL) { + ex_wstr fname = m_base_path; + ex_path_join(fname, false, m_base_fname.c_str(), NULL); + fname += L".dat"; + + m_file_data = ex_fopen(fname, L"wb"); + if (NULL == m_file_data) + { + EXLOGE("[ssh] can not open record data-file for write.\n"); + return false; + } + + m_header_changed = true; + } + + fwrite(m_cache.data(), m_cache.size(), 1, m_file_data); + fflush(m_file_data); + m_cache.empty(); + + return _save_to_info_file(); +} + +bool TppTelnetRec::_save_to_cmd_file() +{ + if (m_cmd_cache.size() == 0) + return true; + + if (NULL == m_file_cmd) { + ex_wstr fname = m_base_path; + ex_path_join(fname, false, m_base_fname.c_str(), NULL); + fname += L"-cmd.txt"; + m_file_cmd = ex_fopen(fname, L"wb"); + if (NULL == m_file_cmd) + { + EXLOGE("[ssh] can not open record cmd-file for write.\n"); + return false; + } + + m_header_changed = true; + } + + fwrite(m_cmd_cache.data(), m_cmd_cache.size(), 1, m_file_cmd); + fflush(m_file_cmd); + m_cmd_cache.empty(); + + return _save_to_info_file(); +} diff --git a/server/tp_core/protocol/telnet/telnet_recorder.h b/server/tp_core/protocol/telnet/telnet_recorder.h new file mode 100644 index 0000000..c7fab7c --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_recorder.h @@ -0,0 +1,53 @@ +#ifndef __TELNET_RECORDER_H__ +#define __TELNET_RECORDER_H__ + +#include "../../common/base_record.h" + +#define TS_RECORD_TYPE_TELNET_TERM_SIZE 0x01 // 终端大小(行数与列数) +#define TS_RECORD_TYPE_TELNET_DATA 0x02 // 用于展示的数据内容 + +#pragma pack(push,1) + +// 记录窗口大小改变的数据包 +typedef struct TS_RECORD_WIN_SIZE +{ + ex_u16 width; + ex_u16 height; +}TS_RECORD_WIN_SIZE; + +#pragma pack(pop) + +class TppTelnetRec : public TppRecBase +{ +public: + TppTelnetRec(); + virtual ~TppTelnetRec(); + +// void record_time_begin(void); // 指定从此时开始计时,之前收到的包会计时为0,这样播放时会快进到此处。 + void record(ex_u8 type, const ex_u8* data, size_t size); + void record_win_size(int width, int height); + + void save_record(); + +protected: + bool _on_begin(const TPP_CONNECT_INFO* info); + bool _on_end(); + + bool _save_to_info_file(); + bool _save_to_data_file(); + bool _save_to_cmd_file(); + +protected: + TS_RECORD_HEADER m_head; + bool m_header_changed; + + MemBuffer m_cmd_cache; + + bool m_save_full_header; + + FILE* m_file_info; + FILE* m_file_data; + FILE* m_file_cmd; +}; + +#endif // __TELNET_RECORDER_H__ diff --git a/server/tp_core/protocol/telnet/telnet_session.cpp b/server/tp_core/protocol/telnet/telnet_session.cpp new file mode 100644 index 0000000..935a718 --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_session.cpp @@ -0,0 +1,742 @@ +#include "telnet_session.h" +#include "telnet_proxy.h" +#include "tpp_env.h" +#include + +#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 + + +TelnetSession::TelnetSession(TelnetProxy *proxy) : + m_proxy(proxy), + m_conn_info(NULL) +{ + m_client_type = 0; + m_state = TP_SESS_STAT_RUNNING; + m_db_id = 0; + m_is_relay = false; + m_is_closed = false; + + m_is_putty_mode = false; + m_is_putty_eat_username = false; + + m_username_sent = false; + m_password_sent = false; + + m_conn_server = new TelnetConn(this, false); + m_conn_client = new TelnetConn(this, true); + + m_status = s_client_connect; + + m_client_addr = "unknown-ip"; +} + +TelnetSession::~TelnetSession() { + delete m_conn_client; + delete m_conn_server; + +// mbedtls_rsa_free(&m_server_pubkey); +// mbedtls_rsa_free(&m_proxy_keypair_dynamic); + + if (NULL != m_conn_info) { + g_telnet_env.free_connect_info(m_conn_info); + } + + EXLOGD("[telnet] session destroy.\n"); +} + +void TelnetSession::save_record() { + m_rec.save_record(); +} + +void TelnetSession::_session_error(int err_code) { + int db_id = 0; + if (!g_telnet_env.session_begin(m_conn_info, &db_id) || db_id == 0) + { + EXLOGE("[telnet] can not write session error to database.\n"); + return; + } + + g_telnet_env.session_end(m_sid.c_str(), db_id, err_code); +} + +bool TelnetSession::_on_session_begin() { + if (!g_telnet_env.session_begin(m_conn_info, &m_db_id)) { + EXLOGE("[telnet] can not save to database, session begin failed.\n"); + return false; + } + + if (!g_telnet_env.session_update(m_db_id, m_conn_info->protocol_sub_type, TP_SESS_STAT_STARTED)) { + EXLOGE("[telnet] can not update state, session begin failed.\n"); + return false; + } + + m_rec.begin(g_telnet_env.replay_path.c_str(), L"tp-telnet", m_db_id, m_conn_info); + + return true; +} + +bool TelnetSession::_on_session_end() +{ + if (m_db_id > 0) + { + // 如果会话过程中没有发生错误,则将其状态改为结束,否则记录下错误值 + if (m_state == TP_SESS_STAT_RUNNING || m_state == TP_SESS_STAT_STARTED) + m_state = TP_SESS_STAT_END; + + EXLOGD("[telnet] session end with code: %d\n", m_state); + g_telnet_env.session_end(m_sid.c_str(), m_db_id, m_state); + } + + return true; +} + +uv_loop_t *TelnetSession::get_loop(void) { + return m_proxy->get_loop(); +} + +void TelnetSession::close(int err_code) { + _do_close(err_code); +} + +void TelnetSession::do_next(TelnetConn *conn, sess_state status) { + if (m_status < s_close) + m_status = status; + + do_next(conn); +} + +void TelnetSession::do_next(TelnetConn *conn) { + sess_state new_status; + ASSERT(m_status != s_dead); + + switch (m_status) { + case s_noop: + return; + + case s_client_connect: + new_status = _do_client_connect(conn); + break; + + case s_negotiation_with_client: + new_status = _do_negotiation_with_client(conn); + break; +// case s_ssl_handshake_with_client: // 与客户端端进行SSL握手 +// new_status = _do_ssl_handshake_with_client(); +// break; +// case s_connect_server: +// new_status = _do_connect_server(); +// break; + case s_server_connected: + new_status = _do_server_connected(); + break; +// case s_negotiation_with_server: +// new_status = _do_negotiation_with_server(); +// break; +// case s_ssl_handshake_with_server: +// new_status = _do_ssl_handshake_with_server(); +// break; + case s_relay: + new_status = _do_relay(conn); + break; + + case s_close: + new_status = _do_close(m_state); + break; + case s_closing: + new_status = _do_check_closing(); + break; + case s_all_conn_closed: + new_status = s_dead; + break; + default: + //UNREACHABLE(); + return; + } + + m_status = new_status; + + if (m_status == s_dead) { + EXLOGW("[telnet] try to remove session.\n"); + _on_session_end(); + //m_proxy->session_finished(this); + m_is_closed = true; + m_proxy->clean_session(); + } +} + +/* +关闭一个session的策略: +1. 两个TelnetConn实例各自关闭自身,当自己关闭完成时,调用session的 on_conn_closed(); +2. session->on_conn_closed()被调用时,检查两个conn是否都关闭完成了,如果都关闭了,标记本session为已关闭. +*/ + +sess_state TelnetSession::_do_close(int state) { + EXLOGD("[telnet] _do_close(). m_status=%d\n", m_status); + if (m_status >= s_close) + return m_status; + + if (state == TP_SESS_STAT_END) { + if (!m_is_relay) + m_state = TP_SESS_STAT_ERR_INTERNAL; + else + m_state = state; + } + else { + if (!m_is_relay) + _session_error(state); + m_state = state; + } + EXLOGV("[telnet] close session.\n"); + EXLOGD("[telnet] _do_close(), conn_client::state=%d, conn_server:state=%d\n", m_conn_client->state(), m_conn_server->state()); + + m_conn_client->close(); + m_conn_server->close(); + m_status = s_closing; + return m_status; +} + +sess_state TelnetSession::_do_check_closing() { + if (m_conn_client->state() == TELNET_CONN_STATE_FREE && m_conn_server->state() == TELNET_CONN_STATE_FREE) + return s_all_conn_closed; + else + return s_closing; +} + +void TelnetSession::on_conn_close() { + EXLOGD("[telnet] on_conn_close(), conn_client::state=%d, conn_server:state=%d\n", m_conn_client->state(), m_conn_server->state()); + if (m_conn_client->state() == TELNET_CONN_STATE_FREE && m_conn_server->state() == TELNET_CONN_STATE_FREE) { + m_status = s_all_conn_closed; + do_next(m_conn_client); + } +} + +sess_state TelnetSession::_do_client_connect(TelnetConn* conn) { + // putty会率先发第一个包,SecureCRT会通过脚本发第一个包 + return _do_negotiation_with_client(conn); +} + +sess_state TelnetSession::_do_negotiation_with_client(TelnetConn* conn) { + if (NULL == conn) + return s_negotiation_with_client; + + if (0 == conn->data().size()) + return s_negotiation_with_client; + + MemBuffer mbuf_msg; + mbuf_msg.reserve(128); + MemStream ms_msg(mbuf_msg); + + MemBuffer mbuf_resp; + mbuf_resp.reserve(conn->data().size()); + MemStream ms_resp(mbuf_resp); + + MemBuffer mbuf_sub; + mbuf_sub.reserve(128); + MemStream ms_sub(mbuf_sub); + + + MemStream s(conn->data()); + ex_u8 ch = 0; + ex_u8 ch_cmd = 0; + for (; s.left() > 0;) + { + ch = s.get_u8(); + if (ch == TELNET_IAC) + { + if (s.left() < 2) + return _do_close(TP_SESS_STAT_ERR_BAD_PKG); + + if (mbuf_sub.size() > 0) + { + // 已经得到一个sub negotiation,在处理新的数据前,先处理掉旧的 + ms_sub.reset(); + } + + ch_cmd = s.get_u8(); + if (ch_cmd == TELNET_SB) + { + // SUB NEGOTIATION,变长数据,以 FF F0 结束 + bool have_SE = false; + ex_u8 ch_sub = 0; + for (; s.left() > 0;) + { + ch_sub = s.get_u8(); + if (ch_sub == TELNET_IAC) + { + if (s.left() > 0) + { + if (s.get_u8() == TELNET_SE) + { + have_SE = true; + break; + } + else + return _do_close(TP_SESS_STAT_ERR_BAD_PKG); + } + } + else + { + ms_sub.put_u8(ch_sub); + } + } + + if (!have_SE) + return _do_close(TP_SESS_STAT_ERR_BAD_PKG); + } + else if (ch_cmd == TELNET_DONT) + { + ms_resp.put_u8(TELNET_IAC); + ms_resp.put_u8(TELNET_WONT); + ms_resp.put_u8(s.get_u8()); + } + else if (ch_cmd == TELNET_DO) + { + ms_resp.put_u8(TELNET_IAC); + ms_resp.put_u8(TELNET_WILL); + ms_resp.put_u8(s.get_u8()); + } + else if (ch_cmd == TELNET_WONT) + { + ms_resp.put_u8(TELNET_IAC); + ms_resp.put_u8(TELNET_DONT); + ms_resp.put_u8(s.get_u8()); + } + else if (ch_cmd == TELNET_WILL) + { + ms_resp.put_u8(TELNET_IAC); + ms_resp.put_u8(TELNET_DO); + ms_resp.put_u8(s.get_u8()); + } + else + { + s.skip(1); + } + } + else + { + ms_msg.put_u8(ch); + } + } + + conn->data().empty(); + + if (mbuf_resp.size() > 0) + { + conn->send(mbuf_resp.data(), mbuf_resp.size()); + } + + 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)); + } + } + else if (mbuf_sub.size() > 8) + { + // 可能含有putty的登录用户名信息(就是SID啦) + 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;) + { + if (mbuf_sub.data()[mbuf_sub.size() - 1] == 0x0a || mbuf_sub.data()[mbuf_sub.size() - 1] == 0x0d) + mbuf_sub.size(mbuf_sub.size() - 1); + else + break; + } + + mbuf_sub.append((ex_u8*)"\x00", 1); + m_sid = (char*)mbuf_sub.data(); + } + } + + 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); + } + } + + // 记录日志,会话开始了 +// set_info(sess_info); + + // 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 s_negotiation_with_client; +} + +sess_state TelnetSession::_do_server_connected() { + m_conn_client->data().empty(); + m_conn_server->data().empty(); + + m_status = s_relay; + + if (m_is_putty_mode) + { + ex_u8 _d[] = "\xff\xfb\x1f\xff\xfb\x20\xff\xfb\x18\xff\xfb\x27\xff\xfd\x01\xff\xfb\x03\xff\xfd\x03"; + m_conn_server->send(_d, sizeof(_d) - 1); + } + + EXLOGW("[telnet] enter relay mode.\n"); + + return s_relay; +} + +sess_state TelnetSession::_do_relay(TelnetConn *conn) { + TelnetSession* _this = conn->session(); + bool is_processed = false; + + if (conn->is_server_side()) + { + // 收到了客户端发来的数据 +// if (!_this->m_is_changle_title_sent) +// { +// _this->m_is_changle_title_sent = true; +// ts_astr msg = "\033]0;TP#telnet://"; +// msg += m_server_ip; +// msg += "\007\x0d\x0a"; +// m_conn_client->send((ts_u8*)msg.c_str(), msg.length()); +// } + + if (_this->m_is_putty_mode && !_this->m_is_putty_eat_username) + { + if (_this->_eat_username(m_conn_client, m_conn_server)) + { + _this->m_is_putty_eat_username = true; + _this->m_username_sent = true; + is_processed = true; + } + } + + if (is_processed) + { + m_conn_client->data().empty(); + return s_relay; + } + + m_conn_server->send(m_conn_client->data().data(), m_conn_client->data().size()); + m_conn_client->data().empty(); + } + else + { + // 收到了服务端返回的数据 + + if (!_this->m_username_sent && _this->m_acc_name.length() > 0) + { + if (_this->_parse_find_and_send(m_conn_server, m_conn_client, _this->m_username_prompt.c_str(), _this->m_acc_name.c_str())) + { + _this->m_username_sent = true; + is_processed = true; + } + } + if (!_this->m_password_sent && _this->m_password_prompt.length() > 0) + { + if (_this->_parse_find_and_send(m_conn_server, m_conn_client, _this->m_password_prompt.c_str(), _this->m_acc_secret.c_str())) + { + _this->m_password_sent = true; + is_processed = true; + } + } + + if (is_processed) + { + m_conn_server->data().empty(); + return s_relay; + } + + + // 替换会导致客户端窗口标题改变的数据 + //ts_u8* data = m_conn_server->data().data(); + //size_t len = m_conn_server->data().size(); +// if (len > 5) +// { +// const ts_u8* _begin = memmem(data, len, (const ts_u8*)"\033]0;", 4); +// +// if (NULL != _begin) +// { +// size_t len_before = _begin - data; +// +// const ts_u8* _end = memmem(_begin + 4, len - len_before, (const ts_u8*)"\007", 1); +// if (NULL != _end) +// { +// _end++; +// +// // 这个包中含有改变标题的数据,将标题换为我们想要的 +// size_t len_end = len - (_end - data); +// MemBuffer mbuf; +// +// if (len_before > 0) +// mbuf.append(data, len_before); +// +// mbuf.append((ts_u8*)"\033]0;TP#ssh://", 13); +// mbuf.append((ts_u8*)_this->m_server_ip.c_str(), _this->m_server_ip.length()); +// mbuf.append((ts_u8*)"\007", 1); +// +// if (len_end > 0) +// mbuf.append(_end, len_end); +// +// m_conn_client->send(mbuf.data(), mbuf.size()); +// } +// else +// { +// m_conn_client->send(m_conn_server->data().data(), m_conn_server->data().size()); +// } +// } +// else +// { +// m_conn_client->send(m_conn_server->data().data(), m_conn_server->data().size()); +// } +// } +// else + { + m_conn_client->send(m_conn_server->data().data(), m_conn_server->data().size()); + } + + m_conn_server->data().empty(); + } + + return s_relay; +} + +bool TelnetSession::_parse_find_and_send(TelnetConn* conn_recv, TelnetConn* conn_remote, const char* find, const char* send) +{ + size_t find_len = strlen(find); + size_t send_len = strlen(send); + if (0 == find_len || 0 == send_len) + return false; + + MemBuffer mbuf_msg; + mbuf_msg.reserve(128); + MemStream ms_msg(mbuf_msg); + + MemStream s(conn_recv->data()); + ex_u8 ch = 0; + ex_u8 ch_cmd = 0; + for (; s.left() > 0;) + { + ch = s.get_u8(); + if (ch == TELNET_IAC) + { + if (s.left() < 2) + return false; + + ch_cmd = s.get_u8(); + if (ch_cmd == TELNET_SB) + { + // SUB NEGOTIATION,变长数据,以 FF F0 结束 + bool have_SE = false; + ex_u8 ch_sub = 0; + for (; s.left() > 0;) + { + ch_sub = s.get_u8(); + if (ch_sub == TELNET_IAC) + { + if (s.left() > 0) + { + if (s.get_u8() == TELNET_SE) + { + have_SE = true; + break; + } + else + return false; + } + } + } + + if (!have_SE) + return false; + } + else + { + s.skip(1); + } + } + else + { + ms_msg.put_u8(ch); + } + } + + if (mbuf_msg.size() < find_len) + return false; + + int find_range = mbuf_msg.size() - find_len; + for (int i = 0; i < find_range; ++i) + { + if (0 == memcmp(mbuf_msg.data() + i, find, find_len)) + { + conn_remote->send(conn_recv->data().data(), conn_recv->data().size()); + conn_recv->data().empty(); + + mbuf_msg.empty(); + mbuf_msg.append((ex_u8*)send, send_len); + mbuf_msg.append((ex_u8*)"\x0d\x0a", 2); + conn_recv->send(mbuf_msg.data(), mbuf_msg.size()); + return true; + } + } + + return false; +} + +bool TelnetSession::_eat_username(TelnetConn* conn_recv, TelnetConn* conn_remote) +{ + bool replaced = false; + + MemBuffer mbuf_msg; + mbuf_msg.reserve(128); + MemStream ms_msg(mbuf_msg); + + MemStream s(conn_recv->data()); + ex_u8 ch = 0; + ex_u8 ch_cmd = 0; + for (; s.left() > 0;) + { + ch = s.get_u8(); + if (ch == TELNET_IAC) + { + if (s.left() < 2) + return false; + + ch_cmd = s.get_u8(); + if (ch_cmd == TELNET_SB) + { + size_t _begin = s.offset(); + size_t _end = 0; + + + // SUB NEGOTIATION,变长数据,以 FF F0 结束 + bool have_SE = false; + ex_u8 ch_sub = 0; + for (; s.left() > 0;) + { + _end = s.offset(); + ch_sub = s.get_u8(); + if (ch_sub == 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 <= 8 || 0 != memcmp("\x27\x00\x00\x55\x53\x45\x52\x01", conn_recv->data().data() + _begin, 8)) + { + ms_msg.put_u8(TELNET_IAC); + ms_msg.put_u8(TELNET_SB); + ms_msg.put_bin(conn_recv->data().data() + _begin, len); + ms_msg.put_u8(TELNET_IAC); + ms_msg.put_u8(TELNET_SE); + continue; + } + + // 到这里就找到了客户端发来的用户名,我们将其抛弃掉,发给服务端的是一个无用户名的包,这样 + // 服务端就会提示输入用户名(login:),后续检测到服务端的提示就会发送用户名了。 + + ms_msg.put_u8(TELNET_IAC); + ms_msg.put_u8(TELNET_SB); + ms_msg.put_bin((ex_u8*)"\x27\x00\x00\x55\x53\x45\x52\x01", 8); + + ms_msg.put_bin((ex_u8*)m_acc_name.c_str(), m_acc_name.length()); + + ms_msg.put_u8(TELNET_IAC); + ms_msg.put_u8(TELNET_SE); + + replaced = true; + } + else + { + ms_msg.put_u8(ch); + ms_msg.put_u8(ch_cmd); + ms_msg.put_u8(s.get_u8()); + } + } + else + { + ms_msg.put_u8(ch); + } + } + + if (replaced) + { + conn_remote->send(mbuf_msg.data(), mbuf_msg.size()); + return true; + } + + return false; +} + diff --git a/server/tp_core/protocol/telnet/telnet_session.h b/server/tp_core/protocol/telnet/telnet_session.h new file mode 100644 index 0000000..8874725 --- /dev/null +++ b/server/tp_core/protocol/telnet/telnet_session.h @@ -0,0 +1,128 @@ +#ifndef __TELNET_SESSION_H__ +#define __TELNET_SESSION_H__ + +#include + +#include "telnet_recorder.h" +#include "telnet_conn.h" + +enum sess_state { + s_noop, // 什么都不做,等特定状态满足 + + s_client_connect, // 客户端连接 + s_negotiation_with_client, // 与客户端进行握手,直到得到客户端发来的登录用户名(其实是SessionID) + +// s_connect_server, // 与服务端连接 + s_server_connected, // 成功连接上服务器了 + + s_relay, // 正常转发数据 + + s_close, // 关闭会话 + s_closing, // 正在关闭双方连接 + s_all_conn_closed, // 两端均已关闭 + + s_dead // 可以销毁此会话了 +}; + +class TelnetProxy; + +class TelnetSession +{ +public: + TelnetSession(TelnetProxy* proxy); + virtual ~TelnetSession(); + + TelnetProxy* get_proxy() { return m_proxy; } + + TelnetConn* client() { return m_conn_client; } + TelnetConn* server() { return m_conn_server; } + uv_loop_t* get_loop(); + + //bool is_client_auth_replaced() const { return m_is_client_auth_replaced; } + + void set_state(int state) { m_state = state; } + void close(int err_code); + void on_conn_close(); + + void do_next(TelnetConn* conn); + void do_next(TelnetConn* conn, sess_state status); + + // save record cache into file. be called per 5 seconds. + void save_record(); + void record(ex_u8 type, const ex_u8* data, size_t size) { + m_rec.record(type, data, size); + } + + //static bool init_builtin_keypair(); + + void client_addr(const char* addr) { m_client_addr = addr; } + const char* client_addr() const { return m_client_addr.c_str(); } + + //static void release_builtin_keys(); + + bool is_relay() { return m_is_relay; } + bool is_closed() { return m_is_closed; } + +protected: + // 继承自 TppSessionBase + bool _on_session_begin(); + bool _on_session_end(); + void _session_error(int err_code); + +private: + sess_state _do_client_connect(TelnetConn* conn); + sess_state _do_negotiation_with_client(TelnetConn* conn); + //sess_state _do_ssl_handshake_with_client(); +// sess_state _do_connect_server(); + sess_state _do_server_connected(); + //sess_state _do_negotiation_with_server(); + //sess_state _do_ssl_handshake_with_server(); + sess_state _do_relay(TelnetConn* conn); + //sess_state _relay_dispatch_rdp(TelnetConn* conn_from, TelnetConn* conn_to); + //sess_state _relay_dispatch_ssl(TelnetConn* conn_from, TelnetConn* conn_to); + sess_state _do_close(int err_code); + sess_state _do_check_closing(); + + //bool _replace_server_cert(SC_ConferenceCreateResponse& gcc); + 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); + +private: + int m_state; + int m_client_type; // 1 = mstsc, 2=freerdp + + TPP_CONNECT_INFO* m_conn_info; + int m_db_id; + + bool m_is_relay; // 是否进入relay模式了(只有进入relay模式才会有录像存在) + bool m_is_closed; + + TppTelnetRec m_rec; + + TelnetProxy* m_proxy; + TelnetConn* m_conn_client; // 与真正客户端通讯的连接(自身作为服务端) + TelnetConn* m_conn_server; // 与真正服务端通讯的连接(自身作为客户端) + + ExThreadLock m_lock; + + ex_astr m_sid; + ex_astr m_conn_ip; + ex_u16 m_conn_port; + ex_astr m_acc_name; + ex_astr m_acc_secret; + ex_astr m_username_prompt; + ex_astr m_password_prompt; + + sess_state m_status; + ex_astr m_client_addr; + + bool m_is_putty_mode; + bool m_is_putty_eat_username; +// bool m_is_changle_title_sent; // 连接成功后,可以给客户端发送一个特殊的字符序列,客户端会改变窗口标题(已经在PuTTY/SecureCRT测试过) + + bool m_username_sent; + bool m_password_sent; + +}; + +#endif // __TELNET_SESSION_H__ diff --git a/server/tp_core/protocol/telnet/tpp_env.cpp b/server/tp_core/protocol/telnet/tpp_env.cpp new file mode 100644 index 0000000..fbd6f33 --- /dev/null +++ b/server/tp_core/protocol/telnet/tpp_env.cpp @@ -0,0 +1,33 @@ +#include "tpp_env.h" + +TppTelnetEnv g_telnet_env; + +TppTelnetEnv::TppTelnetEnv() +{} + +TppTelnetEnv::~TppTelnetEnv() +{} + +bool TppTelnetEnv::_on_init(TPP_INIT_ARGS* args) { + ex_path_join(replay_path, false, L"telnet", NULL); + + ExIniSection* ps = args->cfg->GetSection(L"protocol-telnet"); + if (NULL == ps) { + EXLOGE("[telnet] invalid config(2).\n"); + return false; + } + + ex_wstr tmp; + if (!ps->GetStr(L"bind-ip", tmp)) { + bind_ip = TS_RDP_PROXY_HOST; + } + else { + ex_wstr2astr(tmp, bind_ip); + } + + if (!ps->GetInt(L"bind-port", bind_port)) { + bind_port = TS_RDP_PROXY_PORT; + } + + return true; +} diff --git a/server/tp_core/protocol/telnet/tpp_env.h b/server/tp_core/protocol/telnet/tpp_env.h new file mode 100644 index 0000000..9e08754 --- /dev/null +++ b/server/tp_core/protocol/telnet/tpp_env.h @@ -0,0 +1,22 @@ +#ifndef __TPP_ENV_H__ +#define __TPP_ENV_H__ + +#include "../../common/base_env.h" + +class TppTelnetEnv : public TppEnvBase +{ +public: + TppTelnetEnv(); + ~TppTelnetEnv(); + +public: + ex_astr bind_ip; + int bind_port; + +private: + bool _on_init(TPP_INIT_ARGS* args); +}; + +extern TppTelnetEnv g_telnet_env; + +#endif // __TPP_ENV_H__ diff --git a/server/tp_core/protocol/telnet/tptelnet.cpp b/server/tp_core/protocol/telnet/tptelnet.cpp new file mode 100644 index 0000000..ef1913b --- /dev/null +++ b/server/tp_core/protocol/telnet/tptelnet.cpp @@ -0,0 +1,32 @@ +#include "telnet_proxy.h" +#include "tpp_env.h" +#include + +TPP_API ex_rv tpp_init(TPP_INIT_ARGS* init_args) +{ + if (!g_telnet_env.init(init_args)) + return TPE_FAILED; + + return 0; +} + +TPP_API ex_rv tpp_start(void) +{ + if (!g_telnet_proxy.init()) + return TPE_FAILED; + if (!g_telnet_proxy.start()) + return TPE_FAILED; + + return 0; +} + +TPP_API ex_rv tpp_stop(void) +{ + g_telnet_proxy.stop(); + return 0; +} + +TPP_API void tpp_timer(void) { + // be called per one second. + g_telnet_proxy.timer(); +} diff --git a/server/tp_core/protocol/telnet/tptelnet.vs2015.sln b/server/tp_core/protocol/telnet/tptelnet.vs2015.sln new file mode 100644 index 0000000..89b3573 --- /dev/null +++ b/server/tp_core/protocol/telnet/tptelnet.vs2015.sln @@ -0,0 +1,22 @@ +锘 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tptelnet", "tptelnet.vs2015.vcxproj", "{FDA16D20-09B7-45AF-ADF1-DAF3EF2C0531}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FDA16D20-09B7-45AF-ADF1-DAF3EF2C0531}.Debug|x86.ActiveCfg = Debug|Win32 + {FDA16D20-09B7-45AF-ADF1-DAF3EF2C0531}.Debug|x86.Build.0 = Debug|Win32 + {FDA16D20-09B7-45AF-ADF1-DAF3EF2C0531}.Release|x86.ActiveCfg = Release|Win32 + {FDA16D20-09B7-45AF-ADF1-DAF3EF2C0531}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj b/server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj new file mode 100644 index 0000000..c7c30cf --- /dev/null +++ b/server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj @@ -0,0 +1,234 @@ +锘 + + + + Debug + Win32 + + + Release + Win32 + + + + {FDA16D20-09B7-45AF-ADF1-DAF3EF2C0531} + Win32Proj + tptelnet + 8.1 + tptelnet + + + + DynamicLibrary + true + Unicode + v140_xp + + + DynamicLibrary + false + v140_xp + true + Unicode + + + + + + + + + + + + + + + true + ..\..\..\..\out\server\$(PlatformTarget)\$(Configuration)\ + ..\..\..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ + + + false + ..\..\..\..\out\server\$(PlatformTarget)\$(Configuration)\ + ..\..\..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;_USRDLL;TPP_EXPORTS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + ..\..\..\..\common\teleport;..\..\..\..\common\libex\include;..\..\..\..\external\mbedtls\include;..\..\..\..\external\libuv\include;..\..\..\..\external\libuv\src;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Windows + ..\..\..\..\external\openssl\out32;%(AdditionalLibraryDirectories) + Debug + + + + + Level3 + + + MaxSpeed + true + true + WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;_USRDLL;TPP_EXPORTS;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) + ..\..\..\..\common\teleport;..\..\..\..\common\libex\include;..\..\..\..\external\mbedtls\include;..\..\..\..\external\libuv\include;..\..\..\..\external\libuv\src;%(AdditionalIncludeDirectories) + MultiThreaded + + + Windows + true + true + true + ..\..\..\..\external\openssl\out32;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(IntDir)mbedtls_error.obj + $(IntDir)mbedtls_error.obj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj.filters b/server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj.filters new file mode 100644 index 0000000..4429103 --- /dev/null +++ b/server/tp_core/protocol/telnet/tptelnet.vs2015.vcxproj.filters @@ -0,0 +1,422 @@ +锘 + + + + {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 + + + {c33b46b3-8d5d-4dff-851b-4a9394b198d0} + + + {e3918a55-cbc1-4d69-bed8-c0146f2f4ce2} + + + {c248e06b-21a4-4f6d-b0ca-0f1b2a5783bd} + + + {89181d75-3db3-45a5-a35d-9083fb349de3} + + + {bedac06f-83d5-4cd3-832d-0bce55c3dc52} + + + + + 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 + + + main app + + + common + + + libuv\header + + + libuv\header + + + libuv\header + + + libuv\header + + + libuv\header + + + libuv\header + + + libuv\src + + + libuv\src + + + libuv\src + + + libuv\src\win + + + libuv\src\win + + + common + + + + + 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 + + + main app + + + common + + + libuv\src + + + libuv\src + + + libuv\src + + + libuv\src + + + libuv\src + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + libuv\src\win + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + mbedtls + + + \ No newline at end of file diff --git a/server/www/teleport/static/js/asset/host-list.js b/server/www/teleport/static/js/asset/host-list.js index 393261e..51ed881 100644 --- a/server/www/teleport/static/js/asset/host-list.js +++ b/server/www/teleport/static/js/asset/host-list.js @@ -19,9 +19,9 @@ $app.on_init = function (cb_stack) { upload_file_message: $('#upload-file-message') }; - $tp.assist_checked = function() { + $tp.assist_checked = function () { console.log("---------"); - if($tp.assist.running) { + if ($tp.assist.running) { $app.dom.assist_ver.html($tp.assist.version); } else { $app.dom.assist_ver.html('鏈兘妫娴嬪埌'); @@ -1380,6 +1380,8 @@ $app.create_dlg_edit_account = function () { dlg.field_username = ''; dlg.field_password = ''; dlg.field_pri_key = ''; + dlg.field_prompt_username = ''; + dlg.field_prompt_password = ''; dlg.protocol_sub_type = 0; dlg.dom = { @@ -1475,7 +1477,7 @@ $app.create_dlg_edit_account = function () { html.push(''); if (dlg.host.router_ip.length === 0) { - if(_.isNull(dlg.account)) + if (_.isNull(dlg.account)) dlg.dom.protocol_port.val(3389); else dlg.dom.protocol_port.val(dlg.account.protocol_port); @@ -1491,7 +1493,7 @@ $app.create_dlg_edit_account = function () { html.push(''); if (dlg.host.router_ip.length === 0) { - if(_.isNull(dlg.account)) + if (_.isNull(dlg.account)) dlg.dom.protocol_port.val(22); else dlg.dom.protocol_port.val(dlg.account.protocol_port); @@ -1507,10 +1509,16 @@ $app.create_dlg_edit_account = function () { html.push(''); if (dlg.host.router_ip.length === 0) { - if(_.isNull(dlg.account)) + if (_.isNull(dlg.account)) { dlg.dom.protocol_port.val(23); - else + dlg.dom.prompt_username.val('ogin:'); + dlg.dom.prompt_password.val('assword:'); + } + else { dlg.dom.protocol_port.val(dlg.account.protocol_port); + dlg.dom.prompt_username.val(dlg.account.username_prompt); + dlg.dom.prompt_password.val(dlg.account.password_prompt); + } } dlg.protocol_sub_type = TP_PROTOCOL_TYPE_TELNET_SHELL; @@ -1527,12 +1535,20 @@ $app.create_dlg_edit_account = function () { if (dlg.field_auth === TP_AUTH_TYPE_PASSWORD) { dlg.dom.block_password.show(); dlg.dom.block_sshkey.hide(); + if (dlg.field_protocol === TP_PROTOCOL_TYPE_TELNET) { + dlg.dom.block_prompt.show(); + if(dlg.dom.prompt_username.val().length === 0 && dlg.account.username_prompt.length === 0) + dlg.dom.prompt_username.val('ogin:'); + if(dlg.dom.prompt_password.val().length === 0 && dlg.account.password_prompt.length === 0) + dlg.dom.prompt_password.val('assword:'); + } } else if (dlg.field_auth === TP_AUTH_TYPE_PRIVATE_KEY) { dlg.dom.block_password.hide(); dlg.dom.block_sshkey.show(); } else if (dlg.field_auth === TP_AUTH_TYPE_NONE) { dlg.dom.block_password.hide(); dlg.dom.block_sshkey.hide(); + dlg.dom.block_prompt.hide(); } }; @@ -1564,6 +1580,8 @@ $app.create_dlg_edit_account = function () { dlg.field_username = dlg.dom.username.val(); dlg.field_password = dlg.dom.password.val(); dlg.field_pri_key = dlg.dom.ssh_prikey.val(); + dlg.field_prompt_username = dlg.dom.prompt_username.val(); + dlg.field_prompt_password = dlg.dom.prompt_password.val(); if (dlg.host.router_ip.length === 0) { if (dlg.dom.protocol_port.val().length === 0) { @@ -1589,18 +1607,30 @@ $app.create_dlg_edit_account = function () { return false; } + if (dlg.field_protocol !== TP_PROTOCOL_TYPE_TELNET) { + dlg.field_prompt_username = ''; + dlg.field_prompt_password = ''; + } + if (dlg.field_auth_type === TP_AUTH_TYPE_PASSWORD) { if (dlg.field_id === -1 && dlg.field_password.length === 0) { dlg.dom.password.focus(); $tp.notify_error('璇峰~鍐欑櫥褰曡繙绋嬩富鏈虹殑瀵嗙爜锛'); return false; } + dlg.field_pri_key = ''; } else if (dlg.field_auth_type === TP_AUTH_TYPE_PRIVATE_KEY) { if (dlg.field_id === -1 && dlg.field_pri_key.length === 0) { dlg.dom.ssh_prikey.focus(); $tp.notify_error('璇峰~鍐欑櫥褰曡繙绋嬩富鏈虹殑SSH绉侀挜锛'); return false; } + dlg.field_password = ''; + } else if (dlg.field_auth_type === TP_AUTH_TYPE_NONE) { + dlg.field_prompt_username = ''; + dlg.field_prompt_password = ''; + dlg.field_password = ''; + dlg.field_pri_key = ''; } return true; @@ -1625,7 +1655,9 @@ $app.create_dlg_edit_account = function () { auth_type: dlg.field_auth_type, username: dlg.field_username, password: dlg.field_password, - pri_key: dlg.field_pri_key + pri_key: dlg.field_pri_key, + username_prompt: dlg.field_prompt_username, + password_prompt: dlg.field_prompt_password } }, function (ret) { @@ -1670,7 +1702,9 @@ $app.create_dlg_edit_account = function () { auth_type: dlg.field_auth_type, username: dlg.field_username, password: dlg.field_password, - pri_key: dlg.field_pri_key + pri_key: dlg.field_pri_key, + username_prompt: dlg.field_prompt_username, + password_prompt: dlg.field_prompt_password }, function () { // func_success diff --git a/server/www/teleport/static/js/ops/remote-list.js b/server/www/teleport/static/js/ops/remote-list.js index c3bba1f..0c6e1cc 100644 --- a/server/www/teleport/static/js/ops/remote-list.js +++ b/server/www/teleport/static/js/ops/remote-list.js @@ -121,7 +121,7 @@ $app.on_table_host_cell_created = function (tbl, row_id, col_key, cell_obj) { } else if (action === 'ssh') { $app.connect_remote(uni_id, acc_id, host_id, TP_PROTOCOL_TYPE_SSH, protocol_sub_type); } else if (action === 'telnet') { - $tp.notify_error('灏氭湭瀹炵幇锛'); + $app.connect_remote(uni_id, acc_id, host_id, TP_PROTOCOL_TYPE_TELNET, TP_PROTOCOL_TYPE_TELNET_SHELL); } }); } @@ -240,7 +240,9 @@ $app.on_table_host_render_created = function (render) { } act_btn.push(''); } else if (acc.protocol_type === TP_PROTOCOL_TYPE_TELNET) { + act_btn.push('
'); act_btn.push(''); + act_btn.push('
'); } } diff --git a/server/www/teleport/view/asset/host-list.mako b/server/www/teleport/view/asset/host-list.mako index 843a402..d76fc52 100644 --- a/server/www/teleport/view/asset/host-list.mako +++ b/server/www/teleport/view/asset/host-list.mako @@ -267,7 +267,7 @@ diff --git a/server/www/teleport/webroot/app/controller/account.py b/server/www/teleport/webroot/app/controller/account.py index ddf8db9..ac4ecfc 100644 --- a/server/www/teleport/webroot/app/controller/account.py +++ b/server/www/teleport/webroot/app/controller/account.py @@ -221,6 +221,8 @@ class DoUpdateAccountHandler(TPBaseJsonHandler): param['username'] = args['param']['username'].strip() param['password'] = args['param']['password'] param['pri_key'] = args['param']['pri_key'].strip() + param['username_prompt'] = args['param']['username_prompt'].strip() + param['password_prompt'] = args['param']['password_prompt'].strip() except: log.e('\n') return self.write_json(TPE_PARAM) diff --git a/server/www/teleport/webroot/app/controller/ops.py b/server/www/teleport/webroot/app/controller/ops.py index e4bb78d..3237509 100644 --- a/server/www/teleport/webroot/app/controller/ops.py +++ b/server/www/teleport/webroot/app/controller/ops.py @@ -167,6 +167,8 @@ class DoGetSessionIDHandler(TPBaseJsonHandler): password = args['password'] pri_key = args['pri_key'] protocol_port = int(args['protocol_port']) + username_prompt = args['username_prompt'] + password_prompt = args['password_prompt'] except: return self.write_json(TPE_PARAM) @@ -183,6 +185,9 @@ class DoGetSessionIDHandler(TPBaseJsonHandler): acc_info['password'] = password acc_info['pri_key'] = pri_key + acc_info['username_prompt'] = username_prompt + acc_info['password_prompt'] = password_prompt + conn_info['_enc'] = 0 if acc_id == -1: @@ -219,8 +224,8 @@ class DoGetSessionIDHandler(TPBaseJsonHandler): conn_info['acc_id'] = acc_id conn_info['acc_username'] = acc_info['username'] - conn_info['username_prompt'] = '' - conn_info['password_prompt'] = '' + conn_info['username_prompt'] = acc_info['username_prompt'] + conn_info['password_prompt'] = acc_info['password_prompt'] conn_info['protocol_flag'] = 1 conn_info['protocol_type'] = acc_info['protocol_type'] @@ -231,13 +236,15 @@ class DoGetSessionIDHandler(TPBaseJsonHandler): conn_info['acc_secret'] = acc_info['password'] elif acc_info['auth_type'] == TP_AUTH_TYPE_PRIVATE_KEY: conn_info['acc_secret'] = acc_info['pri_key'] + else: + conn_info['acc_secret'] = '' with tmp_conn_id_lock: global tmp_conn_id_base tmp_conn_id_base += 1 conn_id = tmp_conn_id_base - # log.v(conn_info) + log.v('CONN-INFO:', conn_info) tp_session().set('tmp-conn-info-{}'.format(conn_id), conn_info, 10) req = {'method': 'request_session', 'param': {'conn_id': conn_id}} diff --git a/server/www/teleport/webroot/app/model/account.py b/server/www/teleport/webroot/app/model/account.py index f2a809b..5c2746c 100644 --- a/server/www/teleport/webroot/app/model/account.py +++ b/server/www/teleport/webroot/app/model/account.py @@ -11,7 +11,7 @@ from app.base.stats import tp_stats def get_account_info(acc_id): s = SQL(get_db()) # s.select_from('acc', ['id', 'password', 'pri_key', 'state', 'host_ip', 'router_ip', 'router_port', 'protocol_type', 'protocol_port', 'auth_type', 'username'], alt_name='a') - s.select_from('acc', ['id', 'password', 'pri_key', 'state', 'host_id', 'protocol_type', 'protocol_port', 'auth_type', 'username'], alt_name='a') + s.select_from('acc', ['id', 'password', 'pri_key', 'state', 'host_id', 'protocol_type', 'protocol_port', 'auth_type', 'username', 'username_prompt', 'password_prompt'], alt_name='a') s.where('a.id={}'.format(acc_id)) err = s.query() if err != TPE_OK: @@ -37,7 +37,7 @@ def get_host_accounts(host_id): # 鑾峰彇鎸囧畾涓绘満鐨勬墍鏈夎处鍙 s = SQL(get_db()) # s.select_from('acc', ['id', 'state', 'host_ip', 'router_ip', 'router_port', 'protocol_type', 'protocol_port', 'auth_type', 'username', 'pri_key'], alt_name='a') - s.select_from('acc', ['id', 'state', 'protocol_type', 'protocol_port', 'auth_type', 'username', 'pri_key'], alt_name='a') + s.select_from('acc', ['id', 'state', 'protocol_type', 'protocol_port', 'auth_type', 'username', 'username_prompt', 'password_prompt'], alt_name='a') s.where('a.host_id={}'.format(host_id)) s.order_by('a.username', True) @@ -165,7 +165,7 @@ def get_accounts(sql_filter, sql_order, sql_limit, sql_restrict, sql_exclude): s = SQL(db) # s.select_from('acc', ['id', 'host_id', 'host_ip', 'router_ip', 'router_port', 'username', 'protocol_type', 'auth_type', 'state'], alt_name='a') - s.select_from('acc', ['id', 'host_id', 'username', 'protocol_type', 'auth_type', 'state'], alt_name='a') + s.select_from('acc', ['id', 'host_id', 'username', 'protocol_type', 'auth_type', 'state', 'username_prompt', 'password_prompt'], alt_name='a') str_where = '' _where = list() @@ -253,13 +253,13 @@ def add_account(handler, host_id, args): if db_ret is not None and len(db_ret) > 0: return TPE_EXISTS, 0 - sql = 'INSERT INTO `{}acc` (host_id, host_ip, router_ip, router_port, protocol_type, protocol_port, state, auth_type, username, password, pri_key, creator_id, create_time) VALUES ' \ - '({host_id}, "{host_ip}", "{router_ip}", {router_port}, {protocol_type}, {protocol_port}, {state}, {auth_type}, "{username}", "{password}", "{pri_key}", {creator_id}, {create_time});' \ + sql = 'INSERT INTO `{}acc` (host_id, host_ip, router_ip, router_port, protocol_type, protocol_port, state, auth_type, username, username_prompt, password_prompt, password, pri_key, creator_id, create_time) VALUES ' \ + '({host_id}, "{host_ip}", "{router_ip}", {router_port}, {protocol_type}, {protocol_port}, {state}, {auth_type}, "{username}", "{username_prompt}", "{password_prompt}", "{password}", "{pri_key}", {creator_id}, {create_time});' \ ''.format(db.table_prefix, host_id=host_id, host_ip=args['host_ip'], router_ip=args['router_ip'], router_port=args['router_port'], protocol_type=args['protocol_type'], protocol_port=args['protocol_port'], state=TP_STATE_NORMAL, - auth_type=args['auth_type'], username=args['username'], password=args['password'], pri_key=args['pri_key'], - creator_id=operator['id'], create_time=_time_now) + auth_type=args['auth_type'], username=args['username'], username_prompt=args['username_prompt'], password_prompt=args['password_prompt'], + password=args['password'], pri_key=args['pri_key'], creator_id=operator['id'], create_time=_time_now) # sql = 'INSERT INTO `{}acc` (host_id, protocol_type, protocol_port, state, auth_type, username, password, pri_key, creator_id, create_time) VALUES ' \ # '({host_id}, {protocol_type}, {protocol_port}, {state}, {auth_type}, "{username}", "{password}", "{pri_key}", {creator_id}, {create_time});' \ @@ -311,6 +311,8 @@ def update_account(handler, host_id, acc_id, args): _set.append('protocol_port={}'.format(args['protocol_port'])) _set.append('auth_type={}'.format(args['auth_type'])) _set.append('username="{}"'.format(args['username'])) + _set.append('username_prompt="{}"'.format(args['username_prompt'])) + _set.append('password_prompt="{}"'.format(args['password_prompt'])) if args['auth_type'] == TP_AUTH_TYPE_PASSWORD and len(args['password']) > 0: _set.append('password="{}"'.format(args['password']))