mirror of https://github.com/tp4a/teleport
web版RDP录像播放,可以播放鼠标移动的录像了,下一步是显示rdp的图像。
parent
9305953218
commit
39c8fd32d5
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
|
@ -161,10 +161,9 @@ $app.on_table_host_cell_created = function (tbl, row_id, col_key, cell_obj) {
|
|||
var action = $(this).attr('data-action');
|
||||
|
||||
if (action === 'replay') {
|
||||
//$app.dlg_edit_host.show_edit(row_id);
|
||||
if (row_data.protocol_type === TP_PROTOCOL_TYPE_RDP) {
|
||||
// $tp.notify_error('sorry, not impl.');
|
||||
$app.do_replay_rdp(row_data.id, row_data.user_username, row_data.acc_username, row_data.host_ip, row_data.time_begin);
|
||||
// $app.do_replay_rdp(row_data.id, row_data.user_username, row_data.acc_username, row_data.host_ip, row_data.time_begin);
|
||||
window.open('/audit/replay/' + row_data.protocol_type + '/' + row_data.id);
|
||||
} else if (row_data.protocol_type === TP_PROTOCOL_TYPE_SSH) {
|
||||
window.open('/audit/replay/' + row_data.protocol_type + '/' + row_data.id);
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,29 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
$app.req_record_data = function (record_id, offset) {
|
||||
$tp.ajax_post_json('/audit/get-record-data', {id: record_id, offset: offset},
|
||||
function (ret) {
|
||||
if (ret.code === TPE_OK) {
|
||||
// console.log('data', ret.data);
|
||||
$app.record_data = $app.record_data.concat(ret.data.data_list);
|
||||
$app.record_data_offset += ret.data.data_size;
|
||||
|
||||
if ($app.record_data.length < $app.record_hdr.pkg_count) {
|
||||
$app.req_record_data(record_id, $app.record_data_offset);
|
||||
}
|
||||
} else {
|
||||
$app.dom.status.text("读取录像数据失败:" + tp_error_msg(ret.code));
|
||||
$tp.notify_error('读取录像数据失败:' + tp_error_msg(ret.code, ret.message));
|
||||
console.log('req_record_info error ', ret.code);
|
||||
}
|
||||
},
|
||||
function () {
|
||||
console.log('req_record_info error');
|
||||
},
|
||||
30 * 1000
|
||||
);
|
||||
};
|
||||
|
||||
$app.on_init = function (cb_stack) {
|
||||
var record_id = $app.options.record_id;
|
||||
|
||||
|
@ -70,7 +46,7 @@ $app.on_init = function (cb_stack) {
|
|||
|
||||
Terminal.cursorBlink = false;
|
||||
|
||||
$tp.ajax_post_json('/audit/get-record-header', {id: record_id},
|
||||
$tp.ajax_post_json('/audit/get-record-header', {protocol: TP_PROTOCOL_TYPE_SSH, id: record_id},
|
||||
function (ret) {
|
||||
if (ret.code === TPE_OK) {
|
||||
$app.record_hdr = ret.data;
|
||||
|
@ -184,6 +160,30 @@ $app.on_init = function (cb_stack) {
|
|||
cb_stack.exec();
|
||||
};
|
||||
|
||||
$app.req_record_data = function (record_id, offset) {
|
||||
$tp.ajax_post_json('/audit/get-record-data', {protocol: TP_PROTOCOL_TYPE_SSH, id: record_id, offset: offset},
|
||||
function (ret) {
|
||||
if (ret.code === TPE_OK) {
|
||||
// console.log('data', ret.data);
|
||||
$app.record_data = $app.record_data.concat(ret.data.data_list);
|
||||
$app.record_data_offset += ret.data.data_size;
|
||||
|
||||
if ($app.record_data.length < $app.record_hdr.pkg_count) {
|
||||
$app.req_record_data(record_id, $app.record_data_offset);
|
||||
}
|
||||
} else {
|
||||
$app.dom.status.text("读取录像数据失败:" + tp_error_msg(ret.code));
|
||||
$tp.notify_error('读取录像数据失败:' + tp_error_msg(ret.code, ret.message));
|
||||
console.log('req_record_info error ', ret.code);
|
||||
}
|
||||
},
|
||||
function () {
|
||||
console.log('req_record_info error');
|
||||
},
|
||||
30 * 1000
|
||||
);
|
||||
};
|
||||
|
||||
$app.init_and_play = function() {
|
||||
if (_.isNull($app.player_console_term)) {
|
||||
$app.player_console_term = new Terminal({
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<%!
|
||||
page_title_ = '录像回放'
|
||||
%>
|
||||
|
||||
<%inherit file="../page_single_base.mako"/>
|
||||
|
||||
<%block name="extend_js_file">
|
||||
## <script type="text/javascript" src="${ static_url('plugins/xterm/xterm.js') }"></script>
|
||||
|
||||
<script type="text/javascript" src="${ static_url('js/audit/replay-rdp.js') }"></script>
|
||||
</%block>
|
||||
|
||||
<%block name="extend_css_file">
|
||||
## <link href="${ static_url('plugins/xterm/xterm.css') }" rel="stylesheet" type="text/css"/>
|
||||
</%block>
|
||||
|
||||
<%block name="embed_css">
|
||||
<style type="text/css">
|
||||
#screen {
|
||||
margin-top: 5px;
|
||||
##background-color: #547cff;
|
||||
background-color: #000;
|
||||
border: 1px solid #525252;
|
||||
border-right-color: #fff;
|
||||
border-bottom-color: #fff;
|
||||
## padding: 1px;
|
||||
}
|
||||
</style>
|
||||
</%block>
|
||||
|
||||
<%block name="page_header">
|
||||
<div class="container-fluid top-navbar">
|
||||
<div class="breadcrumb-container">
|
||||
<ol class="breadcrumb">
|
||||
<li><i class="fa fa-server"></i> ${self.attr.page_title_}</li>
|
||||
<li class="sub-title"><span id="recorder-info"></span></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
|
||||
|
||||
<div class="page-content">
|
||||
<div id="toolbar" style="display: inline-block;">
|
||||
<button id="btn-play" type="button" class="btn btn-primary btn-sm" style="width:80px;"><i class="fa fa-pause fa-fw"> 暂停</i></button>
|
||||
<button id="btn-restart" type="button" class="btn btn-success btn-sm"><i class="fa fa-refresh fa-fw"></i> 重新播放</button>
|
||||
|
||||
<button id="btn-speed" type="button" class="btn btn-info btn-sm" style="width:80px;">正常速度</button>
|
||||
|
||||
<button id="btn-big-font" type="button" class="btn btn-default btn-sm"><i class="fa fa-font fa-fw"></i>+</button>
|
||||
<button id="btn-small-font" type="button" class="btn btn-default btn-sm"><i class="fa fa-font fa-fw"></i>-</button>
|
||||
|
||||
<div style="display:inline-block;position:relative;top:4px;margin-left:10px;margin-right:15px;">
|
||||
<span id="btn-skip" style="cursor:pointer;"><i class="fa fa-check-square-o fa-fw"></i> 跳过无操作时间</span>
|
||||
</div>
|
||||
|
||||
<span id="play-status" class="badge badge-normal" style="margin-left:5px;">正在获取数据</span>
|
||||
<span id="play-time" class="badge badge-success" style="margin-left:5px;">总时长:未知</span>
|
||||
</div>
|
||||
<input id="progress" type="range" value="0" min=0 max=100 style="margin-top: 10px;"/>
|
||||
## <div id="xterm-box"></div>
|
||||
|
||||
<div id="screen"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<%block name="extend_content">
|
||||
|
||||
</%block>
|
||||
|
||||
|
||||
|
||||
<%block name="embed_js">
|
||||
<script type="text/javascript">
|
||||
$app.add_options(${page_param});
|
||||
</script>
|
||||
</%block>
|
|
@ -494,7 +494,8 @@ class ReplayHandler(TPBaseHandler):
|
|||
def get(self, protocol, record_id):
|
||||
protocol = int(protocol)
|
||||
if protocol == TP_PROTOCOL_TYPE_RDP:
|
||||
return
|
||||
param = {'record_id': record_id}
|
||||
self.render('audit/replay-rdp.mako', page_param=json.dumps(param))
|
||||
elif protocol == TP_PROTOCOL_TYPE_SSH:
|
||||
param = {'record_id': record_id}
|
||||
self.render('audit/replay-ssh.mako', page_param=json.dumps(param))
|
||||
|
@ -603,12 +604,13 @@ class DoGetRecordHeaderHandler(TPBaseJsonHandler):
|
|||
return self.write_json(TPE_JSON_FORMAT)
|
||||
|
||||
try:
|
||||
protocol_type = int(args['protocol'])
|
||||
record_id = int(args['id'])
|
||||
except:
|
||||
log.e('\n')
|
||||
return self.write_json(TPE_PARAM)
|
||||
|
||||
header, err = record.read_record_head(record_id)
|
||||
header, err = record.read_record_head(protocol_type, record_id)
|
||||
if header is None:
|
||||
return self.write_json(err)
|
||||
|
||||
|
@ -627,13 +629,19 @@ class DoGetRecordDataHandler(TPBaseJsonHandler):
|
|||
return self.write_json(TPE_JSON_FORMAT)
|
||||
|
||||
try:
|
||||
protocol_type = int(args['protocol'])
|
||||
record_id = int(args['id'])
|
||||
offset = int(args['offset'])
|
||||
except:
|
||||
log.e('\n')
|
||||
return self.write_json(TPE_PARAM)
|
||||
|
||||
data_list, data_size, err = record.read_record_data(record_id, offset)
|
||||
if protocol_type == TP_PROTOCOL_TYPE_RDP:
|
||||
data_list, data_size, err = record.read_rdp_record_data(record_id, offset)
|
||||
elif protocol_type == TP_PROTOCOL_TYPE_RDP:
|
||||
data_list, data_size, err = record.read_ssh_record_data(record_id, offset)
|
||||
else:
|
||||
self.write_json(TPE_NOT_EXISTS)
|
||||
self.write_json(err, data={'data_list': data_list, 'data_size': data_size})
|
||||
|
||||
|
||||
|
|
|
@ -114,12 +114,26 @@ def get_records(handler, sql_filter, sql_order, sql_limit, sql_restrict, sql_exc
|
|||
return err, s.total_count, s.recorder
|
||||
|
||||
|
||||
def read_record_head(record_id):
|
||||
def _remove_padding_space(s):
|
||||
r = []
|
||||
for i in range(len(s)):
|
||||
if s[i] == 0x00:
|
||||
break
|
||||
r.append(s[i])
|
||||
return bytearray(r)
|
||||
|
||||
|
||||
def read_record_head(protocol_type, record_id):
|
||||
if not tp_cfg().core.detected:
|
||||
return None, TPE_NO_CORE_SERVER
|
||||
|
||||
record_path = os.path.join(tp_cfg().core.replay_path, 'ssh', '{:09d}'.format(int(record_id)))
|
||||
header_file_path = os.path.join(record_path, 'tp-ssh.tpr')
|
||||
if protocol_type == TP_PROTOCOL_TYPE_RDP:
|
||||
path_name = 'rdp'
|
||||
elif protocol_type == TP_PROTOCOL_TYPE_SSH:
|
||||
path_name = 'ssh'
|
||||
|
||||
record_path = os.path.join(tp_cfg().core.replay_path, path_name, '{:09d}'.format(int(record_id)))
|
||||
header_file_path = os.path.join(record_path, 'tp-{}.tpr'.format(path_name))
|
||||
|
||||
if not os.path.exists(header_file_path):
|
||||
return None, TPE_NOT_EXISTS
|
||||
|
@ -156,22 +170,22 @@ def read_record_head(record_id):
|
|||
# offset += 4
|
||||
|
||||
user_name, = struct.unpack_from('64s', data, offset)
|
||||
user_name = user_name.decode()
|
||||
user_name = _remove_padding_space(user_name).decode()
|
||||
offset += 64
|
||||
account, = struct.unpack_from('64s', data, offset)
|
||||
account = account.decode()
|
||||
account = _remove_padding_space(account).decode()
|
||||
offset += 64
|
||||
|
||||
host_ip, = struct.unpack_from('40s', data, offset)
|
||||
host_ip = host_ip.decode()
|
||||
host_ip = _remove_padding_space(host_ip).decode()
|
||||
offset += 40
|
||||
conn_ip, = struct.unpack_from('40s', data, offset)
|
||||
conn_ip = conn_ip.decode()
|
||||
conn_ip = _remove_padding_space(conn_ip).decode()
|
||||
offset += 40
|
||||
conn_port, = struct.unpack_from('H', data, offset)
|
||||
offset += 2
|
||||
client_ip, = struct.unpack_from('40s', data, offset)
|
||||
client_ip = client_ip.decode()
|
||||
client_ip = _remove_padding_space(client_ip).decode()
|
||||
offset += 40
|
||||
|
||||
except Exception as e:
|
||||
|
@ -197,11 +211,82 @@ def read_record_head(record_id):
|
|||
return header, TPE_OK
|
||||
|
||||
|
||||
def read_record_data(record_id, offset):
|
||||
def read_rdp_record_data(record_id, offset):
|
||||
if not tp_cfg().core.detected:
|
||||
return None, TPE_NO_CORE_SERVER
|
||||
|
||||
record_path = os.path.join(tp_cfg().core.replay_path, 'rdp', '{:09d}'.format(int(record_id)))
|
||||
file_data = os.path.join(record_path, 'tp-rdp.dat')
|
||||
|
||||
if not os.path.exists(file_data):
|
||||
return None, 0, TPE_NOT_EXISTS
|
||||
|
||||
data_list = list()
|
||||
data_size = 0
|
||||
file = None
|
||||
try:
|
||||
file_size = os.path.getsize(file_data)
|
||||
if offset >= file_size:
|
||||
return None, 0, TPE_FAILED
|
||||
|
||||
file = open(file_data, 'rb')
|
||||
if offset > 0:
|
||||
file.seek(offset, io.SEEK_SET)
|
||||
|
||||
# read 1000 packages one time from offset.
|
||||
for i in range(1000):
|
||||
"""
|
||||
// 一个数据包的头
|
||||
typedef struct TS_RECORD_PKG
|
||||
{
|
||||
ex_u8 type; // 包的数据类型
|
||||
ex_u32 size; // 这个包的总大小(不含包头)
|
||||
ex_u32 time_ms; // 这个包距起始时间的时间差(毫秒,意味着一个连接不能持续超过49天)
|
||||
ex_u8 _reserve[3]; // 保留
|
||||
}TS_RECORD_PKG;
|
||||
"""
|
||||
_data = file.read(12)
|
||||
data_size += 12
|
||||
_action, _size, _time, = struct.unpack_from('=BII', _data)
|
||||
if offset + data_size + _size > file_size:
|
||||
return None, 0, TPE_FAILED
|
||||
|
||||
_data = file.read(_size)
|
||||
data_size += _size
|
||||
|
||||
temp = dict()
|
||||
temp['a'] = _action
|
||||
temp['t'] = _time
|
||||
if _action == 0x10:
|
||||
# this is mouse movement event.
|
||||
x, y = struct.unpack_from('HH', _data)
|
||||
temp['x'] = x
|
||||
temp['y'] = y
|
||||
elif _action == 0x11:
|
||||
# this is a data package.
|
||||
_data = base64.b64encode(_data)
|
||||
temp['d'] = _data.decode()
|
||||
else:
|
||||
return None, 0, TPE_FAILED
|
||||
|
||||
data_list.append(temp)
|
||||
if offset + data_size == file_size:
|
||||
break
|
||||
|
||||
except Exception:
|
||||
log.e('failed to read record file: {}\n'.format(file_data))
|
||||
return None, 0, TPE_FAILED
|
||||
finally:
|
||||
if file is not None:
|
||||
file.close()
|
||||
|
||||
return data_list, data_size, TPE_OK
|
||||
|
||||
|
||||
def read_ssh_record_data(record_id, offset):
|
||||
if not tp_cfg().core.detected:
|
||||
return None, TPE_NO_CORE_SERVER
|
||||
|
||||
# read 1000 packages one time from offset.
|
||||
record_path = os.path.join(tp_cfg().core.replay_path, 'ssh', '{:09d}'.format(int(record_id)))
|
||||
file_data = os.path.join(record_path, 'tp-ssh.dat')
|
||||
|
||||
|
@ -220,6 +305,7 @@ def read_record_data(record_id, offset):
|
|||
if offset > 0:
|
||||
file.seek(offset, io.SEEK_SET)
|
||||
|
||||
# read 1000 packages one time from offset.
|
||||
for i in range(1000):
|
||||
"""
|
||||
// 一个数据包的头
|
||||
|
|
Loading…
Reference in New Issue