Initial Commit

This commit is contained in:
Christian Risi
2025-06-02 17:41:15 +02:00
commit c3bc1d993e
15 changed files with 2354 additions and 0 deletions

12
src/main.ts Normal file
View File

@@ -0,0 +1,12 @@
import { SuricataSocketStream } from "./socket-server/server.ts";
// UGLY: take them from
const UNIXPATH = "/var/lib/suricata/eve.sock"
const WEB_SOCKET_URI = ""
const app = new SuricataSocketStream(
UNIXPATH,
WEB_SOCKET_URI
)
app.serve()

View File

@@ -0,0 +1,125 @@
import { JSON_BytesToken } from "../utils/json_byte_repr.ts";
// TODO: make this an iterator
export class JSON_Chunker {
private jsons: Array<string> = [];
private chunks: Array<Uint8Array> = [];
private totalBytes = 0;
private _openBrackets = 0;
private _openArray = 0;
private decoder = new TextDecoder("utf-8");
private debug = true;
private get delimiterToken() {
return this.openArray + this.openBrackets
}
private get openBrackets() {
return this._openBrackets;
}
private set openBrackets(value) {
this._openBrackets = value;
if (this._openBrackets < 0) {
this._openBrackets = 0;
}
}
private get openArray() {
return this._openArray;
}
private set openArray(value) {
this._openArray = value;
if (this._openArray < 0) {
this._openArray = 0;
}
}
public get messageReady() {
return (this.jsons.length !== 0);
}
public get message() {
const msg = this.jsons.shift();
return msg;
}
public pushChunk(chunk: Uint8Array) {
const chunkLen = chunk.length;
let lastByteRead = 0;
for (let i = 0; i < chunkLen; i++) {
const byte = chunk[i];
switch (byte) {
case JSON_BytesToken.OPEN_ARR:
this.openArray = this.openArray + 1;
break;
case JSON_BytesToken.CLOSE_ARR:
this.openArray = this.openArray - 1;
break;
case JSON_BytesToken.OPEN_BRACKET:
this.openBrackets = this.openBrackets + 1;
break;
case JSON_BytesToken.CLOSE_BRACKET:
this.openBrackets = this.openBrackets - 1;
break;
default:
continue;
}
// If we are in this situation, then we have a JSON
if (this.delimiterToken === 0) {
const _chunk = chunk.slice(lastByteRead, i + 1);
// console.log(this.decoder.decode(_chunk));
this.insertChunk(_chunk);
this.flush();
lastByteRead = i + 1;
}
}
// Otherwise insert the remaining chunk
this.insertChunk(
chunk.slice(lastByteRead),
);
}
private insertChunk(chunk: Uint8Array) {
this.totalBytes += chunk.length;
this.chunks.push(chunk);
}
private flush() {
let remainingBytes = this.totalBytes;
let writtenBytes = 0;
const msg = new Uint8Array(this.totalBytes);
for (let chunk of this.chunks) {
const validBytes = Math.min(
remainingBytes,
chunk.length,
);
chunk = chunk.slice(0, validBytes);
msg.set(chunk, writtenBytes);
writtenBytes += validBytes;
remainingBytes -= validBytes;
}
this.totalBytes = 0;
this.chunks = []
const message = this.decoder.decode(msg);
this.jsons.push(message);
}
}

View File

@@ -0,0 +1,75 @@
import { delay } from "../utils/delay.ts";
import { JSON_Chunker } from "./json_chunker.ts";
export class SuricataSocketStream {
private listener: Deno.UnixListener;
private maxBlobSize: number = 1024;
private decoder: TextDecoder = new TextDecoder("utf-8");
private webSocket: WebSocket;
constructor(
unixSocketPath: string,
webSocketURI: string,
) {
this.listener = Deno.listen({
path: unixSocketPath,
transport: "unix",
});
this.webSocket = new WebSocket(webSocketURI);
}
public async serve() {
// Await for connections
for await (const conn of this.listener) {
while (this.webSocket.readyState !== this.webSocket.OPEN) {
await delay(20);
}
const jsonParser = new JSON_Chunker()
do {
let bytesRead: number | null = 0;
const chunk: Uint8Array = new Uint8Array(this.maxBlobSize);
bytesRead = await conn.read(chunk);
// Connection has ended,
// break the cycle
if (!bytesRead) {
break;
}
jsonParser.pushChunk(chunk.slice(0, bytesRead))
while (jsonParser.messageReady) {
this.messageProcess(jsonParser.message!)
}
} while (true);
}
}
// UGLY: change this to a more flexible method
private messageProcess(msg: string | Uint8Array) {
// this.webSocket.send(msg);
console.log(
`
Debug, found this message:
${msg}
END OF MESSAGE
`
)
}
}

9
src/utils/delay.ts Normal file
View File

@@ -0,0 +1,9 @@
export function delay(ms: number) {
return new Promise(
(resolve) => {
setTimeout(() => {
resolve
}, ms)
}
)
}

View File

@@ -0,0 +1,6 @@
export enum JSON_BytesToken{
OPEN_ARR = 0x5B,
CLOSE_ARR = 0x5D,
OPEN_BRACKET = 0x7B,
CLOSE_BRACKET = 0x7D
}