318 lines
8.5 KiB
TypeScript
318 lines
8.5 KiB
TypeScript
import { onBeforeUnmount, onMounted, reactive } from 'vue';
|
||
import { scriptUrl } from '@/assets/js/wiregasm_worker';
|
||
import { WK, OptionsType } from '@/plugins/wk-worker';
|
||
const wk = new WK();
|
||
|
||
export const NO_SELECTION = { id: '', idx: 0, start: 0, length: 0 };
|
||
|
||
type StateType = {
|
||
/**初始化 */
|
||
initialized: boolean;
|
||
/**pcap信息 */
|
||
summary: {
|
||
filename: string;
|
||
file_type: string;
|
||
file_length: number;
|
||
file_encap_type: string;
|
||
packet_count: number;
|
||
start_time: number;
|
||
stop_time: number;
|
||
elapsed_time: number;
|
||
};
|
||
/**字段 */
|
||
columns: string[];
|
||
/**过滤条件 */
|
||
filter: string;
|
||
/**过滤条件错误信息 */
|
||
filterError: string | null;
|
||
/**当前过滤条件 */
|
||
currentFilter: string;
|
||
/**当前选中的帧编号 */
|
||
selectedFrame: number;
|
||
/**当前选中的帧数据 */
|
||
selectedPacket: { tree: any[]; data_sources: any[] };
|
||
/**pcap包帧数据 */
|
||
packetFrameData: Map<string, any> | null;
|
||
/**当前选中的帧数据-空占位 */
|
||
selectedTreeEntry: typeof NO_SELECTION;
|
||
/**选择帧的Dump数据标签 */
|
||
selectedDataSourceIndex: number;
|
||
/**处理完成状态 */
|
||
finishedProcessing: boolean;
|
||
/**pcap包帧数,匹配帧数 */
|
||
totalFrames: number;
|
||
/**pcap包帧数据 */
|
||
packetFrames: any[];
|
||
/**加载帧数 */
|
||
nextPageSize: number;
|
||
/**加载页数 */
|
||
nextPageNum: number;
|
||
/**加载下一页 */
|
||
nextPageLoad: boolean;
|
||
};
|
||
|
||
export function usePCAP() {
|
||
const state = reactive<StateType>({
|
||
initialized: false,
|
||
summary: {
|
||
filename: '',
|
||
file_type: 'Wireshark/tcpdump/... - pcap',
|
||
file_length: 0,
|
||
file_encap_type: 'Ethernet',
|
||
packet_count: 0,
|
||
start_time: 0,
|
||
stop_time: 0,
|
||
elapsed_time: 0,
|
||
},
|
||
columns: [],
|
||
filter: '',
|
||
filterError: null,
|
||
currentFilter: '',
|
||
selectedFrame: 1,
|
||
/**当前选中的帧数据 */
|
||
selectedPacket: { tree: [], data_sources: [] },
|
||
packetFrameData: null, // 注意:Map 需要额外处理
|
||
selectedTreeEntry: NO_SELECTION, // NO_SELECTION 需要定义
|
||
/**选择帧的Dump数据标签 */
|
||
selectedDataSourceIndex: 0,
|
||
/**处理完成状态 */
|
||
finishedProcessing: false,
|
||
totalFrames: 0,
|
||
packetFrames: [],
|
||
nextPageNum: 1,
|
||
nextPageSize: 40,
|
||
nextPageLoad: false,
|
||
});
|
||
|
||
// 清除帧数据和报文信息状态
|
||
function handleStateReset() {
|
||
// 加载pcap包的数据
|
||
state.nextPageNum = 1;
|
||
// 选择帧的数据
|
||
state.selectedFrame = 0;
|
||
state.selectedPacket = { tree: [], data_sources: [] };
|
||
state.packetFrameData = null;
|
||
state.selectedTreeEntry = NO_SELECTION;
|
||
state.selectedDataSourceIndex = 0;
|
||
}
|
||
|
||
/**解析帧数据为简单结构 */
|
||
function parseFrameData(id: string, node: Record<string, any>) {
|
||
let map = new Map();
|
||
|
||
if (node.tree && node.tree.length > 0) {
|
||
for (let i = 0; i < node.tree.length; i++) {
|
||
const subMap = parseFrameData(`${id}-${i}`, node.tree[i]);
|
||
subMap.forEach((value, key) => {
|
||
map.set(key, value);
|
||
});
|
||
}
|
||
} else if (node.length > 0) {
|
||
map.set(id, {
|
||
id: id,
|
||
idx: node.data_source_idx,
|
||
start: node.start,
|
||
length: node.length,
|
||
});
|
||
}
|
||
|
||
return map;
|
||
}
|
||
|
||
/**帧数据点击选中 */
|
||
function handleSelectedTreeEntry(e: any) {
|
||
console.log('fnSelectedTreeEntry', e);
|
||
state.selectedTreeEntry = e;
|
||
}
|
||
/**报文数据点击选中 */
|
||
function handleSelectedFindSelection(src_idx: number, pos: number) {
|
||
console.log('fnSelectedFindSelection', pos);
|
||
if (state.packetFrameData == null) return;
|
||
// find the smallest one
|
||
let current = null;
|
||
for (let [k, pp] of state.packetFrameData) {
|
||
if (pp.idx !== src_idx) continue;
|
||
|
||
if (pos >= pp.start && pos <= pp.start + pp.length) {
|
||
if (
|
||
current != null &&
|
||
state.packetFrameData.get(current).length > pp.length
|
||
) {
|
||
current = k;
|
||
} else {
|
||
current = k;
|
||
}
|
||
}
|
||
}
|
||
if (current != null) {
|
||
state.selectedTreeEntry = state.packetFrameData.get(current);
|
||
}
|
||
}
|
||
/**包数据表点击选中 */
|
||
function handleSelectedFrame(no: number) {
|
||
console.log('fnSelectedFrame', no, state.totalFrames);
|
||
state.selectedFrame = no;
|
||
wk.send({ type: 'select', number: state.selectedFrame });
|
||
}
|
||
/**包数据表滚动底部加载 */
|
||
function handleScrollBottom() {
|
||
const totalFetched = state.packetFrames.length;
|
||
console.log('fnScrollBottom', totalFetched);
|
||
if (!state.nextPageLoad && totalFetched < state.totalFrames) {
|
||
state.nextPageLoad = true;
|
||
state.nextPageNum++;
|
||
loaldFrames(state.filter, state.nextPageNum);
|
||
}
|
||
}
|
||
/**包数据表过滤 */
|
||
function handleFilterFrames() {
|
||
console.log('fnFilterFinish', state.filter);
|
||
wk.send({ type: 'check-filter', filter: state.filter });
|
||
}
|
||
/**包数据表加载 */
|
||
function loaldFrames(filter: string, page: number = 1) {
|
||
if (!(state.initialized && state.finishedProcessing)) return;
|
||
const limit = state.nextPageSize;
|
||
wk.send({
|
||
type: 'frames',
|
||
filter: filter,
|
||
skip: (page - 1) * limit,
|
||
limit: limit,
|
||
});
|
||
}
|
||
|
||
/**加载包文件 */
|
||
function handleLoadFile(file: File | Blob) {
|
||
state.summary = {
|
||
filename: '',
|
||
file_type: 'Wireshark/tcpdump/... - pcap',
|
||
file_length: 0,
|
||
file_encap_type: 'Ethernet',
|
||
packet_count: 0,
|
||
start_time: 0,
|
||
stop_time: 0,
|
||
elapsed_time: 0,
|
||
};
|
||
state.finishedProcessing = false;
|
||
|
||
wk.send({ type: 'process', file: file });
|
||
}
|
||
|
||
/**本地示例文件 */
|
||
async function handleLoadExample() {
|
||
const name = 'test_ethernet.pcap';
|
||
const res = await fetch('/wiregasm/test_ethernet.pcap');
|
||
const body = await res.arrayBuffer();
|
||
|
||
state.summary = {
|
||
filename: '',
|
||
file_type: 'Wireshark/tcpdump/... - pcap',
|
||
file_length: 0,
|
||
file_encap_type: 'Ethernet',
|
||
packet_count: 0,
|
||
start_time: 0,
|
||
stop_time: 0,
|
||
elapsed_time: 0,
|
||
};
|
||
state.finishedProcessing = false;
|
||
|
||
wk.send({ type: 'process-data', name: name, data: body });
|
||
}
|
||
|
||
/**接收数据后回调 */
|
||
function wkMessage(res: Record<string, any>) {
|
||
switch (res.type) {
|
||
case 'status':
|
||
console.info(res.status);
|
||
break;
|
||
case 'error':
|
||
console.warn(res.error);
|
||
break;
|
||
case 'init':
|
||
wk.send({ type: 'columns' });
|
||
state.initialized = true;
|
||
break;
|
||
case 'columns':
|
||
state.columns = res.data;
|
||
break;
|
||
case 'frames':
|
||
// console.log(res.data);
|
||
const { matched, frames } = res.data;
|
||
state.totalFrames = matched;
|
||
|
||
if (state.nextPageNum == 1) {
|
||
state.packetFrames = frames;
|
||
// 有匹配的选择第一个
|
||
if (frames.length > 0) {
|
||
state.selectedFrame = frames[0].number;
|
||
handleSelectedFrame(state.selectedFrame);
|
||
}
|
||
} else {
|
||
state.packetFrames = state.packetFrames.concat(frames);
|
||
state.nextPageLoad = false;
|
||
}
|
||
break;
|
||
case 'selected':
|
||
state.selectedPacket = res.data;
|
||
state.packetFrameData = parseFrameData('root', res.data);
|
||
state.selectedTreeEntry = NO_SELECTION;
|
||
state.selectedDataSourceIndex = 0;
|
||
break;
|
||
case 'processed':
|
||
// setStatus(`Error: non-zero return code (${e.data.code})`);
|
||
state.finishedProcessing = true;
|
||
if (res.data.code === 0) {
|
||
state.summary = res.data.summary;
|
||
}
|
||
|
||
// 加载数据
|
||
handleStateReset();
|
||
loaldFrames(state.filter);
|
||
break;
|
||
case 'filter':
|
||
const filterRes = res.data;
|
||
if (filterRes.ok) {
|
||
state.currentFilter = state.filter;
|
||
state.filterError = null;
|
||
// 加载数据
|
||
handleStateReset();
|
||
loaldFrames(state.filter);
|
||
} else {
|
||
state.filterError = filterRes.error;
|
||
}
|
||
break;
|
||
default:
|
||
console.warn(res);
|
||
break;
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
// 建立链接
|
||
const options: OptionsType = {
|
||
url: scriptUrl,
|
||
onmessage: wkMessage,
|
||
onerror: (ev: any) => {
|
||
console.error(ev);
|
||
},
|
||
};
|
||
wk.connect(options);
|
||
});
|
||
|
||
onBeforeUnmount(() => {
|
||
wk.send({ type: 'close' }) && wk.close();
|
||
});
|
||
|
||
return {
|
||
state,
|
||
handleSelectedTreeEntry,
|
||
handleSelectedFindSelection,
|
||
handleSelectedFrame,
|
||
handleScrollBottom,
|
||
handleFilterFrames,
|
||
handleLoadExample,
|
||
handleLoadFile,
|
||
};
|
||
}
|
||
export default usePCAP;
|