netkitty
Version:
Network tools kit
150 lines (149 loc) • 6.06 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.PcapParser = void 0;
const events_1 = __importDefault(require("events"));
const node_fs_1 = require("node:fs");
const node_util_1 = require("node:util");
const GLOBAL_HEADER_LENGTH = 24; //bytes
const PACKET_HEADER_LENGTH = 16; //bytes
class PcapParser extends events_1.default {
static parse(input) {
return new PcapParser(input);
}
constructor(input) {
super();
this.errored = false;
this.index = 0;
this.offset = 0;
this.stream = (typeof input === 'string') ? (0, node_fs_1.createReadStream)(input) : input;
this.stream.pause();
this.stream.on('data', (data) => this.onData(data));
this.stream.on('error', (err) => this.onError(err));
this.stream.on('end', () => this.onEnd());
this.buffer = null;
this.state = () => this.parseGlobalHeader();
this.endianness = null;
process.nextTick(this.stream.resume.bind(this.stream));
}
updateBuffer(data) {
if (data === null || data === undefined)
return;
if (this.buffer === null) {
this.buffer = data;
}
else {
let extendedBuffer = Buffer.alloc(this.buffer.length + data.length);
this.buffer.copy(extendedBuffer);
data.copy(extendedBuffer, this.buffer.length);
this.buffer = extendedBuffer;
}
}
parseGlobalHeader() {
const buffer = this.buffer;
if (buffer.length >= GLOBAL_HEADER_LENGTH) {
let msg;
const magicNumber = buffer.toString('hex', 0, 4);
// determine pcap endianness
if (magicNumber == 'a1b2c3d4') {
this.endianness = 'BE';
}
else if (magicNumber == 'd4c3b2a1' || magicNumber == '4d3cb2a1') {
this.endianness = 'LE';
}
else {
this.errored = true;
this.stream.pause();
msg = (0, node_util_1.format)('unknown magic number: %s', magicNumber);
this.emit('error', new Error(msg));
this.onEnd();
return false;
}
const header = {
magicNumber: buffer['readUInt32' + this.endianness](0, true),
majorVersion: buffer['readUInt16' + this.endianness](4, true),
minorVersion: buffer['readUInt16' + this.endianness](6, true),
gmtOffset: buffer['readInt32' + this.endianness](8, true),
timestampAccuracy: buffer['readUInt32' + this.endianness](12, true),
snapshotLength: buffer['readUInt32' + this.endianness](16, true),
linkLayerType: buffer['readUInt32' + this.endianness](20, true)
};
if (header.majorVersion != 2 && header.minorVersion != 4) {
this.errored = true;
this.stream.pause();
msg = (0, node_util_1.format)('unsupported version %d.%d. pcap-parser only parses libpcap file format 2.4', header.majorVersion, header.minorVersion);
this.emit('error', new Error(msg));
this.onEnd();
}
else {
this.emit('globalHeader', header);
this.buffer = buffer.subarray(GLOBAL_HEADER_LENGTH);
this.state = () => this.parsePacketHeader();
this.offset = GLOBAL_HEADER_LENGTH;
return true;
}
}
return false;
}
parsePacketHeader() {
const buffer = this.buffer;
if (buffer.length >= PACKET_HEADER_LENGTH) {
const header = {
timestampSeconds: buffer['readUInt32' + this.endianness](0, true),
timestampMicroseconds: buffer['readUInt32' + this.endianness](4, true),
capturedLength: buffer['readUInt32' + this.endianness](8, true),
originalLength: buffer['readUInt32' + this.endianness](12, true)
};
this.currentPacketHeader = header;
this.emit('packetHeader', header);
this.buffer = buffer.subarray(PACKET_HEADER_LENGTH);
this.state = () => this.parsePacketBody();
this.index += 1;
return true;
}
return false;
}
parsePacketBody() {
const buffer = this.buffer;
if (buffer.length >= this.currentPacketHeader.capturedLength) {
const data = buffer.subarray(0, this.currentPacketHeader.capturedLength);
const packetLength = data.length;
const recordLength = PACKET_HEADER_LENGTH + packetLength;
const pcapPacketInfo = {
index: this.index,
offset: this.offset,
length: recordLength,
recordHeaderOffset: this.offset,
recordHeaderLength: PACKET_HEADER_LENGTH,
packetOffset: this.offset + PACKET_HEADER_LENGTH,
packetLength: data.length,
seconds: this.currentPacketHeader.timestampSeconds,
microseconds: this.currentPacketHeader.timestampMicroseconds,
packet: data.toString('base64')
};
this.emit('packetData', data);
this.emit('packet', pcapPacketInfo);
this.buffer = buffer.subarray(this.currentPacketHeader.capturedLength);
this.state = () => this.parsePacketHeader();
this.offset += recordLength;
return true;
}
return false;
}
onData(data) {
if (this.errored)
return;
this.updateBuffer(data);
while (this.state()) {
}
}
onError(err) {
this.emit('error', err);
}
onEnd() {
this.emit('end');
}
}
exports.PcapParser = PcapParser;