UNPKG

@fibercom/routeros-api

Version:

Robust MikroTik RouterOS API client for Node.js and TypeScript

144 lines 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Receiver = void 0; const iconv = require("iconv-lite"); const debug = require("debug"); const info = debug("routeros-api:connector:receiver:info"); const error = debug("routeros-api:connector:receiver:error"); /** * Responsible for decoding and routing raw RouterOS API socket data * to the appropriate tagged command listeners. */ class Receiver { constructor(socket) { this.tags = new Map(); this.pendingData = Buffer.alloc(0); this.currentSentence = []; this.socket = socket; } /** * Register a callback for a given tag. Each command gets its own tag. */ read(tag, callback) { info("Reader of %s tag is being set", tag); this.tags.set(tag, { name: tag, callback }); } /** * Unregister the callback for a tag once command completes (!done). */ stop(tag) { info("Not reading from %s tag anymore", tag); this.tags.delete(tag); } /** * Handle incoming socket data, decode lines based on RouterOS framing protocol. * Convert win1252-encoded text to UTF-8. */ processRawData(data) { if (data.length === 0) return; this.pendingData = Buffer.concat([this.pendingData, data]); while (this.pendingData.length > 0) { const decodedLength = this.decodeLength(this.pendingData); if (!decodedLength) return; const [descriptorLength, wordLength] = decodedLength; const frameLength = descriptorLength + wordLength; if (this.pendingData.length < frameLength) return; this.pendingData = this.pendingData.subarray(descriptorLength); if (wordLength === 0) { this.dispatchSentence(); continue; } const word = this.pendingData.subarray(0, wordLength); this.pendingData = this.pendingData.subarray(wordLength); this.currentSentence.push(iconv.decode(word, "win1252")); } } /** * Dispatch a complete RouterOS sentence to its tagged channel. */ dispatchSentence() { if (this.currentSentence.length === 0) return; const sentence = this.currentSentence; this.currentSentence = []; if (sentence[0] === "!fatal") { this.socket.emit("fatal", sentence.slice(1)); return; } const tagIndex = sentence.findIndex((word) => word.startsWith(".tag=")); if (tagIndex === -1) { error("Received an untagged sentence: %O", sentence); return; } const currentTag = sentence[tagIndex].substring(5); const packet = sentence.filter((_, index) => index !== tagIndex); const tag = this.tags.get(currentTag); if (tag) { info("Sending to tag %s the packet %O", tag.name, packet); tag.callback(packet); } else { // A final packet can already be in flight when a tagged command is // cancelled. Surface it for diagnostics, but never crash the socket's // data event handler. error("Ignoring packet for unregistered tag %s: %O", currentTag, packet); this.socket.emit("unregisteredTag", currentTag, packet); } } /** * Decode RouterOS variable-length prefix for sentence lengths. * * Supports multi-byte formats depending on the highest bit set. * * @param {Buffer} data * @returns {[number, number]} - [lengthDescriptorSize, sentenceLength] */ decodeLength(data) { if (data.length < 1) return null; const first = data[0]; const descriptorLength = first < 0x80 ? 1 : first < 0xc0 ? 2 : first < 0xe0 ? 3 : first < 0xf0 ? 4 : 5; if (data.length < descriptorLength) return null; let length; switch (descriptorLength) { case 1: length = first; break; case 2: length = (first & 0x3f) * 0x100 + data[1]; break; case 3: length = (first & 0x1f) * 0x10000 + data[1] * 0x100 + data[2]; break; case 4: length = (first & 0x0f) * 0x1000000 + data[1] * 0x10000 + data[2] * 0x100 + data[3]; break; default: length = data[1] * 0x1000000 + data[2] * 0x10000 + data[3] * 0x100 + data[4]; } return [descriptorLength, length]; } } exports.Receiver = Receiver; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVjZWl2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29ubmVjdG9yL1JlY2VpdmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLG9DQUFvQztBQUNwQywrQkFBZ0M7QUFFaEMsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLHNDQUFzQyxDQUFDLENBQUM7QUFDM0QsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLHVDQUF1QyxDQUFDLENBQUM7QUFjN0Q7OztHQUdHO0FBQ0gsTUFBYSxRQUFRO0lBTW5CLFlBQVksTUFBYztRQUpsQixTQUFJLEdBQStCLElBQUksR0FBRyxFQUFFLENBQUM7UUFDN0MsZ0JBQVcsR0FBVyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLG9CQUFlLEdBQWEsRUFBRSxDQUFDO1FBR3JDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNJLElBQUksQ0FBQyxHQUFXLEVBQUUsUUFBb0M7UUFDM0QsSUFBSSxDQUFDLCtCQUErQixFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQzNDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxJQUFJLENBQUMsR0FBVztRQUNyQixJQUFJLENBQUMsaUNBQWlDLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDN0MsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7T0FHRztJQUNJLGNBQWMsQ0FBQyxJQUFZO1FBQ2hDLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQUUsT0FBTztRQUM5QixJQUFJLENBQUMsV0FBVyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FB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