@fibercom/routeros-api
Version:
Robust MikroTik RouterOS API client for Node.js and TypeScript
144 lines • 10.4 kB
JavaScript
"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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