netkitty
Version:
Network tools kit
264 lines (263 loc) • 7.88 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PcapReader = void 0;
const events_1 = __importDefault(require("events"));
const promises_1 = require("node:fs/promises");
const duplexpair_1 = __importDefault(require("duplexpair"));
const PcapParser_1 = require("./PcapParser");
class PcapReader extends events_1.default {
get readStream() {
return this.duplexPair.socket2;
}
get writeStream() {
return this.duplexPair.socket1;
}
constructor(options) {
super();
this.chunkSize = 1518 * 10;
this.index = 0;
this.offset = 0;
this.readDone = false;
this.readBufferFileHandle = null;
this.paused = false;
this.filename = options.filename;
this.watch = !!options.watch;
this.chunkSize = options.chunkSize ? options.chunkSize : this.chunkSize;
if (options.onPacket)
this.onPacket = options.onPacket;
if (options.onStart)
this.onStart = options.onStart;
if (options.onStop)
this.onStop = options.onStop;
if (options.onDone)
this.onDone = options.onDone;
if (options.onError)
this.onError = options.onError;
}
/**
* Pause read
* @protected
*/
pauseRead() {
this.readStream.pause();
this.paused = true;
}
/**
* Resume read
* @protected
*/
resumeRead() {
this.readStream.resume();
this.paused = false;
}
/**
* Initialize reader
* @protected
*/
initReader() {
this.duplexPair = new duplexpair_1.default();
this.parser = PcapParser_1.PcapParser.parse(this.readStream);
this.parser
.on('packet', async (pcapPacketInfo) => {
this.index = pcapPacketInfo.index;
this.emit('packet', pcapPacketInfo);
if (this.onPacket) {
this.pauseRead();
await this.onPacket(pcapPacketInfo);
this.resumeRead();
}
})
.on('error', (err) => {
this.emit('error', err);
if (this.onError)
this.onError(err);
});
}
/**
* Reset reader
* @protected
*/
async reset() {
await this.stop();
this.parser?.removeAllListeners();
this.index = 0;
this.offset = 0;
this.paused = false;
this.initReader();
}
/**
* Initialize read packet file handle
* @protected
*/
async initReadPacketFileHandle() {
return await (0, promises_1.open)(this.filename, 'r');
}
/**
* Initialize read buffer file handle
* @protected
*/
async initReadBufferFileHandle() {
if (!this.readBufferFileHandle)
this.readBufferFileHandle = await (0, promises_1.open)(this.filename, 'r');
return this.readBufferFileHandle;
}
/**
* Read file buffer
* @protected
*/
async readBuffer() {
const readBufferFileHandle = await this.initReadBufferFileHandle();
try {
const result = await readBufferFileHandle.read({
buffer: Buffer.alloc(this.chunkSize),
offset: 0,
length: this.chunkSize,
position: this.offset
});
if (result.bytesRead <= 0)
return true;
const data = Buffer.alloc(result.bytesRead);
result.buffer.copy(data, 0, 0, result.bytesRead);
this.offset += result.bytesRead;
this.writeStream.write(data);
return false;
}
catch (e) {
return true;
}
}
/**
* Read file continually
* @protected
*/
continualRead() {
process.nextTick(async () => {
let read = true;
this.once('stop', () => read = false);
let isReachEnd = false;
while (read || !isReachEnd) {
if (this.paused) {
await new Promise(resolve => setTimeout(resolve, 1));
continue;
}
isReachEnd = await this.readBuffer();
if (isReachEnd) {
await new Promise(resolve => setTimeout(resolve, 1));
}
}
await this.readBufferFileHandle?.close();
this.readBufferFileHandle = null;
this.readDone = true;
this.emit('done');
if (this.onDone)
await this.onDone();
});
}
/**
* Read the file straight
* @protected
*/
straightRead() {
process.nextTick(async () => {
let isReachEnd = false;
let stopped = false;
this.once('stop', () => stopped = true);
while (!isReachEnd && !stopped) {
if (this.paused) {
await new Promise(resolve => setTimeout(resolve, 1));
continue;
}
isReachEnd = await this.readBuffer();
}
this.readDone = true;
if (!stopped)
await this.stop();
await this.readBufferFileHandle?.close();
this.readBufferFileHandle = null;
this.emit('done');
if (this.onDone)
await this.onDone();
});
}
/**
* Read packet data by record's offset and record's length
* @param offset
* @param length
*/
async readPacket(offset, length) {
const fileHandle = await this.initReadPacketFileHandle();
const recordHeaderLength = 16;
const packetLength = length - recordHeaderLength;
const result = await fileHandle.read({
buffer: Buffer.alloc(packetLength),
offset: 0,
length: packetLength,
position: offset + recordHeaderLength
});
await fileHandle.close();
return result.buffer;
}
/**
* Start reading pcap
*/
async start() {
await this.reset();
this.emit('start');
if (this.onStart)
await this.onStart();
if (this.watch) {
this.continualRead();
}
else {
this.straightRead();
}
}
/**
* Stop reading pcap
*/
async stop() {
if (this.parser) {
this.emit('stop');
if (!this.readDone)
await new Promise(resolve => this.once('done', resolve));
if (this.onStop)
await this.onStop();
}
if (this.duplexPair) {
await new Promise(resolve => {
if (this.writeStream.closed)
return resolve();
this.writeStream.once('close', () => resolve());
this.writeStream.once('error', err => !!err);
this.writeStream.destroy();
});
await new Promise(resolve => {
if (this.readStream.closed)
return resolve();
this.readStream.once('close', () => resolve());
this.readStream.once('error', err => !!err);
this.readStream.destroy();
});
}
}
/**
* Close pcap reader
*/
async close() {
await this.stop();
this.emit('close');
this.removeAllListeners();
}
on(eventName, listener) {
super.on(eventName, listener);
return this;
}
once(eventName, listener) {
super.once(eventName, listener);
return this;
}
}
exports.PcapReader = PcapReader;