42 Commits

Author SHA1 Message Date
d0ee1a7044 Fixed gtter payloadSize 2021-11-25 00:19:53 +01:00
57394882cc Implemented reading keyExchangeData and determining handshake-number 2021-11-25 00:19:31 +01:00
b41d728ba9 Fixed missing export 2021-11-25 00:18:37 +01:00
d6135a02e9 Fixed regex 2021-11-25 00:18:21 +01:00
ffe14e3f53 Added hexConverter helper-module 2021-11-24 23:22:09 +01:00
09bea81058 Returning packet not to loose information 2021-11-24 23:13:35 +01:00
66c0fdfe12 Fixed Object.assign 2021-11-24 23:13:04 +01:00
b32b5cbed7 Changed regex to use readbehind/ahead instead of groups 2021-11-24 23:12:47 +01:00
e86b5fca9a Added durationMicros 2021-11-24 22:35:59 +01:00
d2178f3b73 Implemented PacketType Disassociation 2021-11-24 22:35:07 +01:00
fe5dec7860 Implemented PacketType AssociationRequest & Response 2021-11-24 22:29:41 +01:00
0d472e8cb3 Implemented PacketType Authentication 2021-11-24 22:27:22 +01:00
bba5da2599 Moved packet-creation outside for all cases 2021-11-24 22:25:10 +01:00
c2210dfb59 Added isRetry field 2021-11-24 21:11:19 +01:00
3c3ab18410 Revert "Added PacketType ContentionFreePeriod"
This reverts commit 333a514363.
2021-11-24 21:01:17 +01:00
aa9d5431f8 Changed payloadSize to payloadData with payloadSize getter 2021-11-24 20:44:04 +01:00
62b55b94f1 Added missing payloadSize to Packet 2021-11-24 20:42:33 +01:00
025edf8465 Implemented getting payloadData and calculating to bytes 2021-11-24 20:42:10 +01:00
59e7188543 Changed most checks using Optional chaining and Nullish coalescing operator 2021-11-24 20:39:20 +01:00
333a514363 Added PacketType ContentionFreePeriod 2021-11-24 20:35:44 +01:00
d714b342ed Removed debug read statement 2021-11-24 20:35:10 +01:00
1aae9d5e71 Added library luxon 2021-11-24 20:01:33 +01:00
6bc248b667 First implementation of PacketStreamFactory 2021-11-24 20:01:03 +01:00
0cbbecbd2c Added typeMoreData 2021-11-24 20:00:28 +01:00
368b6585ba Now using internal buffer-system 2021-11-24 20:00:05 +01:00
4f1463eb4f Fixed args being wrong way around 2021-11-24 19:40:46 +01:00
d11aac5599 Implemented RegexBlockStream 2021-11-24 15:35:29 +01:00
fa8d630283 Change exec to just return process and streams 2021-11-24 15:35:05 +01:00
9c28ed53e3 Added exec-module 2021-11-24 13:11:01 +01:00
cc8b106157 Added logger-setup-module 2021-11-24 13:10:41 +01:00
8ee189f52b Fixed wrong capitalisation 2021-11-24 01:16:37 +01:00
79efcc3951 Fixed missing import 2021-11-24 01:12:13 +01:00
7dd131d6b5 Implemented dto-obj Packet 2021-11-24 01:11:42 +01:00
1b6721d242 Implemented env defaults and checking 2021-11-24 01:07:29 +01:00
d13df7b03d Add comment for logger 2021-11-24 01:06:44 +01:00
34c73c82c8 Gave logger name 2021-11-24 01:06:29 +01:00
93f78aafe0 Changed || to ?? to detect undefined env 2021-11-24 01:06:09 +01:00
184432f642 Added helper for env-vars 2021-11-24 01:03:40 +01:00
7562ff2268 Added module log4js 2021-11-23 17:05:43 +01:00
20ed3d0928 Docker setup 2021-11-23 15:15:10 +01:00
6ccd02af6c Added start-script 2021-11-23 15:14:55 +01:00
f862c362fe npm init 2021-11-23 15:09:53 +01:00
12 changed files with 685 additions and 0 deletions

3
.dockerignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules
npm-debug.log
.*

13
Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM node:16
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./
RUN npm install
# Bundle app source
COPY ./src/ .
CMD ["npm", "run"]

237
package-lock.json generated Normal file
View File

@@ -0,0 +1,237 @@
{
"name": "rfmon-to-influx",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "rfmon-to-influx",
"version": "1.0.0",
"license": "AGPL-3.0",
"dependencies": {
"@influxdata/influxdb-client": "^1.20.0",
"log4js": "^6.3.0",
"luxon": "^2.1.1"
}
},
"node_modules/@influxdata/influxdb-client": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/@influxdata/influxdb-client/-/influxdb-client-1.20.0.tgz",
"integrity": "sha512-jaKSI63hmQ5VSkJrFJkYIXaKlhoF+mGd4HmOf7v/X7pmEi69ReHp922Wyx6/OeCrpndRMbsadk+XmGNdd43cFw=="
},
"node_modules/date-format": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
"integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==",
"engines": {
"node": ">=4.0"
}
},
"node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/flatted": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA=="
},
"node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
},
"node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/log4js": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
"integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
"dependencies": {
"date-format": "^3.0.0",
"debug": "^4.1.1",
"flatted": "^2.0.1",
"rfdc": "^1.1.4",
"streamroller": "^2.2.4"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/luxon": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.1.1.tgz",
"integrity": "sha512-6VQVNw7+kQu3hL1ZH5GyOhnk8uZm21xS7XJ/6vDZaFNcb62dpFDKcH8TI5NkoZOdMRxr7af7aYGrJlE/Wv0i1w==",
"engines": {
"node": ">=12"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
},
"node_modules/streamroller": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
"integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
"dependencies": {
"date-format": "^2.1.0",
"debug": "^4.1.1",
"fs-extra": "^8.1.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/streamroller/node_modules/date-format": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
"integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
"engines": {
"node": ">=4.0"
}
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"engines": {
"node": ">= 4.0.0"
}
}
},
"dependencies": {
"@influxdata/influxdb-client": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/@influxdata/influxdb-client/-/influxdb-client-1.20.0.tgz",
"integrity": "sha512-jaKSI63hmQ5VSkJrFJkYIXaKlhoF+mGd4HmOf7v/X7pmEi69ReHp922Wyx6/OeCrpndRMbsadk+XmGNdd43cFw=="
},
"date-format": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
"integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w=="
},
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "2.1.2"
}
},
"flatted": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA=="
},
"fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
},
"graceful-fs": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz",
"integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"requires": {
"graceful-fs": "^4.1.6"
}
},
"log4js": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
"integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
"requires": {
"date-format": "^3.0.0",
"debug": "^4.1.1",
"flatted": "^2.0.1",
"rfdc": "^1.1.4",
"streamroller": "^2.2.4"
}
},
"luxon": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.1.1.tgz",
"integrity": "sha512-6VQVNw7+kQu3hL1ZH5GyOhnk8uZm21xS7XJ/6vDZaFNcb62dpFDKcH8TI5NkoZOdMRxr7af7aYGrJlE/Wv0i1w=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
},
"streamroller": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
"integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
"requires": {
"date-format": "^2.1.0",
"debug": "^4.1.1",
"fs-extra": "^8.1.0"
},
"dependencies": {
"date-format": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
"integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA=="
}
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
}
}
}

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "rfmon-to-influx",
"version": "1.0.0",
"description": "Writing (mostly meta-) data received in Wireless-Monitor-Mode into an InfluxDB",
"main": "src/main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node src/main.js"
},
"repository": {
"type": "git",
"url": "https://gitea.ruekov.eu/Ruakij/rfmon-to-influx"
},
"author": "Ruakij",
"license": "AGPL-3.0",
"dependencies": {
"@influxdata/influxdb-client": "^1.20.0",
"log4js": "^6.3.0",
"luxon": "^2.1.1"
}
}

95
src/dto/Packet.js Normal file
View File

@@ -0,0 +1,95 @@
const PacketType = {
Beacon: 'Beacon',
ProbeRequest: 'ProbeRequest',
ProbeResponse: 'ProbeResponse',
Data: 'Data',
MoreData: 'MoreData',
RequestToSend: 'RequestToSend',
ClearToSend: 'ClearToSend',
Acknowledgment: 'Acknowledgment',
BlockAcknowledgment: 'BlockAcknowledgment',
NoData: 'NoData',
Authentication: 'Authentication',
AssociationRequest: 'AssociationRequest',
AssociationResponse: 'AssociationResponse',
Disassociation: 'Disassociation',
Handshake: 'Handshake',
Unknown: 'Unknown'
}
class Packet{
timestampMicros;
isRetry;
srcMac;
dstMac;
bssid;
signal;
frequency;
dataRate;
durationMicros;
payloadData;
get payloadSize(){
return this.payloadData.length;
}
packetType;
}
// Extensions of Packet
class PacketWithSSID extends Packet{
ssid;
}
class BeaconPacket extends PacketWithSSID{}
class ProbeRequestPacket extends PacketWithSSID{}
class ProbeResponsePacket extends PacketWithSSID{}
const AuthenticationType = {
OpenSystem_1: 'OpenSystem_1',
OpenSystem_2: 'OpenSystem_2',
Unknown: 'Unknown',
}
class AuthenticationPacket extends Packet{
authenticationType;
}
class AssociationRequestPacket extends PacketWithSSID{}
class AssociationResponsePacket extends Packet{
associationIsSuccessful;
}
class DisassociationPacket extends Packet{
disassociationReason;
}
const HandshakeStage = {
1: '1',
2: '2',
3: '3',
4: '4'
}
class HandshakePacket extends Packet{
handshakeStage;
}
// Specify exports
module.exports = {
PacketType,
Packet,
PacketWithSSID,
BeaconPacket,
ProbeRequestPacket,
ProbeResponsePacket,
AuthenticationType,
AuthenticationPacket,
AssociationRequestPacket,
AssociationResponsePacket,
DisassociationPacket,
HandshakeStage,
HandshakePacket,
};

13
src/helper/env.js Normal file
View File

@@ -0,0 +1,13 @@
function requireEnvVars(requiredEnv){
// Ensure required ENV vars are set
let unsetEnv = requiredEnv.filter((env) => !(typeof process.env[env] !== 'undefined'));
if (unsetEnv.length > 0) {
return "Required ENV variables are not set: [" + unsetEnv.join(', ') + "]";
}
}
// Specify exports
module.exports = {
requireEnvVars
};

23
src/helper/exec.js Normal file
View File

@@ -0,0 +1,23 @@
const logger = require("./logger.js")("exec");
const { spawn } = require("child_process");
function exec(cmd, stdout, stderr, exit_handler){
const [bin, ...args] = cmd.split(' ')
logger.addContext("binary", "bin");
logger.debug(`Spawn process '${cmd}'`);
let proc = spawn(bin, args);
return {
"process": proc,
"stdout": proc.stdout,
"stderr": proc.stderr
}
}
// Specify exports
module.exports = {
exec
};

View File

@@ -0,0 +1,24 @@
// From https://stackoverflow.com/a/34356351
// Convert a hex string to a byte array
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
// Specify exports
module.exports = {
hexToBytes,
bytesToHex
}

11
src/helper/logger.js Normal file
View File

@@ -0,0 +1,11 @@
const log4js = require("log4js");
function setup(category = "unknown"){
const logger = log4js.getLogger(category);
logger.level = process.env.LOGLEVEL ?? "INFO";
return logger;
}
// Specify exports
module.exports = setup;

24
src/main.js Normal file
View File

@@ -0,0 +1,24 @@
const logger = require("./helper/logger.js")("main");
const { requireEnvVars } = require("./helper/env.js");
const { exit } = require("process");
/// Setup ENVs
const env = process.env;
// Defaults
{
env.LOGLEVEL ??= "INFO";
env.WIFI_INTERFACE ??= "wlan0";
env.WIFI_CHANNEL ??= [1,6,11];
env.WIFI_CHANNEL_TIME ??= 1;
}
// Required vars
let errorMsg = requireEnvVars([
"INFLUX_URL", "INFLUX_TOKEN",
"INFLUX_ORG", "INFLUX_BUCKET"
]);
if(errorMsg){
logger.fatal(errorMsg);
exit(1);
}

View File

@@ -0,0 +1,155 @@
const logger = require.main.require("./helper/logger.js")("PacketStreamFactory");
const { Transform } = require('stream');
const { DateTime } = require("luxon");
const { PacketType, Packet, PacketWithSSID, BeaconPacket, ProbeRequestPacket, ProbeResponsePacket, AuthenticationPacket, AuthenticationType, AssociationResponsePacket, DisassociationPacket, HandshakePacket, HandshakeStage } = require.main.require('./dto/Packet.js');
const hexConv = require.main.require("./helper/hexConverter.js");
const PACKET_TYPE_MAP = {
"Beacon": PacketType.Beacon,
"Probe Request": PacketType.ProbeRequest,
"Probe Response": PacketType.ProbeResponse,
"Data": PacketType.Data,
"More Data": PacketType.MoreData,
"Request-To-Send": PacketType.RequestToSend,
"Clear-To-Send": PacketType.ClearToSend,
"Acknowledgment": PacketType.Acknowledgment,
"BA": PacketType.BlockAcknowledgment,
"Authentication": PacketType.Authentication,
"Assoc Request": PacketType.AssociationRequest,
"Assoc Response": PacketType.AssociationResponse,
"Disassociation:": PacketType.Disassociation,
"EAPOL": PacketType.Handshake,
};
const PACKET_TYPES_REGEX = Object.keys(PACKET_TYPE_MAP).join('|');
const AUTHENTICATION_TYPE_MAP = {
"(Open System)-1": AuthenticationType.OpenSystem_1,
"(Open System)-2": AuthenticationType.OpenSystem_2,
}
/**
* Read data from text-blocks and convert them to Packet
*/
class PacketStreamFactory extends Transform{
matcher;
withholdLastBlock;
matchAllOnFlush;
constructor(){
super({
readableObjectMode: true,
writableObjectMode: true
});
}
_transform(chunk, encoding, next){
let packet = new Packet();
const lines = chunk.split('\n');
const header = lines.splice(0, 1)[0]; // Grab first line, 'lines' is now the payload
packet = this._handleHeader(packet, header);
packet = this._handlePayload(packet, lines);
logger.debug(packet);
next(null, packet); // Get next chunk
}
_handleHeader(packet, data){
// Convert time to epoch-micros Unfortunately luxon doesnt use micros, but millis as smallest time-unit requiring some "hacks"
packet.timestampMicros = DateTime.fromISO(data.slice(0, 12)).toSeconds() + data.slice(12, 15)/1000000;
packet.isRetry = data.match(/(?<=^|\s)Retry(?=$|\s)/i)? true: false;
packet.dataRate = Number(data.match(/(?<=^|\s)[0-9]+(\.[0-9]+)?(?=\sMb\/?s($|\s))/i)?.[0]) || null;
packet.frequency = Number(data.match(/(?<=^|\s)[0-9]{4}(?=\sMHz($|\s))/i)?.[0]) || null;
packet.durationMicros = Number(data.match(/(?<=^|\s)[0-9]{1,4}(?=us($|\s))/i)?.[0]) || null;
packet.signal = Number(data.match(/(?<=^|\s)-[0-9]{2,3}(?=dBm\sSignal($|\s))/i)?.[0]) || null;
let packetTypeStr = data.match(new RegExp('(?<=^|\\s)('+ PACKET_TYPES_REGEX +')(?=$|\\s)', 'i'))?.[0];
packet.packetType = packetTypeStr? PACKET_TYPE_MAP[packetTypeStr]: PacketType.Unknown;
packet.srcMac = data.match(/(?<=(^|\s)(SA|TA):).{17}(?=$|\s)/i)?.[0] ?? null;
packet.dstMac = data.match(/(?<=(^|\s)(DA|RA):).{17}(?=$|\s)/i)?.[0] ?? null;
packet.bssid = data.match(/(?<=(^|\s)BSSID:).{17}(?=$|\s)/i)?.[0] ?? null;
// Cover special cases with more data
let newPacket;
switch(packet.packetType){
case PacketType.Beacon:
case PacketType.ProbeRequest:
case PacketType.ProbeResponse:
case PacketType.AssociationRequest:
newPacket = new PacketWithSSID();
newPacket.ssid = data.match(new RegExp('(?<=(^|\\s)'+ packetTypeStr +'\\s\\().{0,32}(?=\\)($|\\s))', 'i'))?.[0] ?? null;
break;
case PacketType.Authentication:
newPacket = new AuthenticationPacket();
newPacket.authenticationType = AUTHENTICATION_TYPE_MAP[data.match(/(?<=(^|\s)Authentication\s).{3,}(?=\:(\s|$))/i)[0]] ?? AuthenticationType.Unknown;
break;
case PacketType.AssociationResponse:
newPacket = new AssociationResponsePacket();
newPacket.associationIsSuccessful = data.match(/(?<=(^|\s)Assoc\sResponse\s.{0,30})Successful(?=\s|$)/i) ? true : false;
break;
case PacketType.Disassociation:
newPacket = new DisassociationPacket();
newPacket.disassociationReason = data.match(/(?<=(^|\s)Disassociation:\s).*$/i)?.[0] ?? null;
break;
}
if(newPacket) packet = Object.assign(newPacket, packet); // Use new, more specific, packet and copy old data over
return packet;
}
_handlePayload(packet, data){
data = data.join('');
// Get payload-Hex-Data. If there is no data: empty
packet.payloadData = hexConv.hexToBytes(data.match(/(?<=\s)([A-F0-9]{1,4}(?!(\.|x)))/igm)?.join('') ?? '');
// Cover special cases with more data
let newPacket;
switch(packet.packetType){
case PacketType.Handshake:
newPacket = new HandshakePacket();
// Read key-information
let keyInfoRaw = (packet.payloadData[0x5]<<0x8) + packet.payloadData[0x6];
let keyInfo = {
"KeyDescriptorVersion": keyInfoRaw>>0 & 0b111,
"KeyType": keyInfoRaw>>3 & 0b1,
"KeyIndex": keyInfoRaw>>4 & 0b11,
"Install": keyInfoRaw>>6 & 0b1,
"KeyACK": keyInfoRaw>>7 & 0b1,
"KeyMIC": keyInfoRaw>>8 & 0b1,
"Secure": keyInfoRaw>>9 & 0b1,
"Error": keyInfoRaw>>10 & 0b1,
"Request": keyInfoRaw>>11 & 0b1,
"EncryptedKeyData": keyInfoRaw>>12 & 0b1,
"SMKMessage": keyInfoRaw>>13 & 0b1,
};
newPacket.handshakeStage = (!keyInfo.Install && keyInfo.KeyACK && !keyInfo.KeyMIC && !keyInfo.Secure)? HandshakeStage[1] :
(!keyInfo.Install && !keyInfo.KeyACK && keyInfo.KeyMIC && !keyInfo.Secure)? HandshakeStage[2] :
( keyInfo.Install && keyInfo.KeyACK && keyInfo.KeyMIC && keyInfo.Secure)? HandshakeStage[3] :
(!keyInfo.Install && !keyInfo.KeyACK && keyInfo.KeyMIC && keyInfo.Secure)? HandshakeStage[4] :
null;
break;
}
if(newPacket) packet = Object.assign(newPacket, packet);
return packet;
}
}
// Specify exports
module.exports = {
PacketStreamFactory
};

View File

@@ -0,0 +1,66 @@
const logger = require.main.require("./helper/logger.js")("RegexBlockStream");
const { Transform } = require('stream')
/**
* Matches whole blocks as regex and passes them on
*/
class RegexBlockStream extends Transform{
matcher;
withholdLastBlock;
matchAllOnFlush;
/**
* @param {RegExp} matcher Block-match - WARNING: It should match a clean-block (including e.g. newline)! Otherwise buffer will get dirty and use more and more ressources.
* @param {boolean} withholdLastBlock When true, the last matches block will not be submitted to prevent submitting incomplete blocks.
* @param {boolean} matchAllOnFlush (Only in combination with withholdLastBlock) When enabled, the buffer will be matched on last time on _flush (stream deconstruction) and write any, also incomplete, blocks
*/
constructor(matcher, withholdLastBlock = true, matchAllOnFlush = false){
super({
readableObjectMode: true,
writableObjectMode: true
});
this.matcher = matcher;
this.withholdLastBlock = withholdLastBlock;
this.matchAllOnFlush = matchAllOnFlush;
}
_transform(chunk, encoding, next){
chunk = this.readableBuffer.length? this.readableBuffer.join() + chunk: chunk; // Add previous buffer to current chunk
this.readableBuffer.length && this.readableBuffer.clear(); // Clear buffer once we read it
let matches = chunk.match(this.matcher); // Match
if(matches){
if(this.withholdLastBlock) matches.pop(); // Remove last if we want to withhold it
chunk = this._writeMatches(matches, chunk);
}
this.readableBuffer.push(chunk); // Store remaining data in buffer
next(); // Get next chunk
}
_writeMatches(matches, chunk = null){
if(matches){
matches.forEach((match) => {
this.push(match); // Write match to stream
if(chunk) chunk = chunk.replace(match, ''); // Remove match from chunks
});
}
if(chunk) return chunk;
}
_flush(next){
if(matchAllOnFlush){ // When requested, we'll match one last time over the remaining buffer
let chunk = this.readableBuffer.toString();
let matches = chunk.match(this.matcher); // Match remaining buffer
_writeMatches(matches); // Write matches including last element
}
next(); // Tell system we are done
}
}
// Specify exports
module.exports = {
RegexBlockStream
};