UNPKG

@fibercom/routeros-api

Version:

Robust MikroTik RouterOS API client for Node.js and TypeScript

383 lines 30.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RouterOSAPI = void 0; const Connector_1 = require("./connector/Connector"); const Channel_1 = require("./Channel"); const RosException_1 = require("./RosException"); const RStream_1 = require("./RStream"); const crypto = require("crypto"); const debug = require("debug"); const timers_1 = require("timers"); const events_1 = require("events"); let info; let error; /** * Creates a connection object with the credentials provided */ class RouterOSAPI extends events_1.EventEmitter { constructor(options) { super(); /** Connected flag */ this.connected = false; /** Connecting flag */ this.connecting = false; /** Closing flag */ this.closing = false; this.channelsOpen = 0; this.holdingConnectionWithKeepalive = false; this.connectionHoldInterval = null; this.registeredStreams = []; this.setOptions(options); this.initializeDebuggers(); } setOptions(options) { this.host = options.host; this.user = options.user; this.password = options.password; this.port = options.port || 8728; this.timeout = options.timeout || 10; this.tls = options.tls; this.keepalive = options.keepalive || false; this.debug = options.debug || false; } initializeDebuggers() { if (this.debug) { // Enable debug output by setting the DEBUG environment variable process.env.DEBUG = process.env.DEBUG ? `${process.env.DEBUG},routeros-api:*` : "routeros-api:*"; info = debug("routeros-api:api:info"); error = debug("routeros-api:api:error"); } else { // Create no-op debuggers when debug is disabled info = (...args) => { }; error = (...args) => { }; } } /** * Attempts to connect to the RouterOS device with retry logic. * * This method wraps the `.connect()` method and retries the connection * multiple times if it fails, waiting a specified delay between each attempt. * * @param attempts - Maximum number of connection attempts (default: 3) * @param delay - Delay (in ms) between retries (default: 1000 ms) * @returns A Promise resolving to the Connector instance upon successful connection * @throws The last encountered error if all retry attempts fail */ connectWithRetry() { return __awaiter(this, arguments, void 0, function* (attempts = 3, delay = 1000) { let tries = 1; while (tries <= attempts) { try { // Try connecting if (this.debug) { console.debug(`[DEBUG] Connection attempt: ${tries}`); } return yield this.connect(); } catch (err) { if (this.debug) { console.warn(`[WARN] Connection attempt ${tries} failed: ${err.message}`); } tries++; // If we've exhausted all retries, rethrow the last error if (tries > attempts) { throw err; } // Wait for the specified delay before trying again yield new Promise((res) => setTimeout(res, delay)); } } // This fallback should never hit, but satisfies TypeScript return rules throw new RosException_1.RosException("RETRYFAIL", { message: "Failed to connect after multiple attempts", }); }); } connect() { return __awaiter(this, void 0, void 0, function* () { var _a; if (this.connecting) throw new RosException_1.RosException("ALRDYCONNECTING"); if (this.connected) return this; this.connecting = true; this.connected = false; this.connector = new Connector_1.Connector({ host: this.host, port: this.port, timeout: this.timeout, tls: this.tls, debug: this.debug, }); try { yield this.connector.connect(); // ✅ await critical! yield this.login(); this.connected = true; this.connecting = false; this.connector.once("error", (e) => { this.connected = false; this.connecting = false; this.emit("error", e); }); this.connector.once("timeout", (e) => { this.connected = false; this.connecting = false; this.emit("timeout", e); }); if (this.keepalive) this.keepaliveBy("#"); return this; } catch (err) { this.connecting = false; this.connected = false; (_a = this.connector) === null || _a === void 0 ? void 0 : _a.destroy(); throw err; } }); } write(params, ...moreParams) { params = this.concatParams(params, moreParams); let chann = this.openChannel(); this.holdConnection(); chann.once("close", () => { chann = null; this.decreaseChannelsOpen(); this.releaseConnectionHold(); }); return chann.write(params); } writeStream(params, ...moreParams) { params = this.concatParams(params, moreParams); const stream = new RStream_1.RStream(this.openChannel(), params); stream.on("started", () => this.holdConnection()); stream.on("stopped", () => { this.unregisterStream(stream); this.decreaseChannelsOpen(); this.releaseConnectionHold(); }); stream.start(); this.registerStream(stream); return stream; } stream(params = [], ...moreParams) { let callback = moreParams.pop(); if (typeof callback !== "function") { if (callback) moreParams.push(callback); callback = null; } params = this.concatParams(params, moreParams); const stream = new RStream_1.RStream(this.openChannel(), params, callback); stream.on("started", () => this.holdConnection()); stream.on("stopped", () => { this.unregisterStream(stream); this.decreaseChannelsOpen(); this.releaseConnectionHold(); stream.removeAllListeners(); }); stream.start(); stream.prepareDebounceEmptyData(); this.registerStream(stream); return stream; } keepaliveBy(params = [], ...moreParams) { this.holdingConnectionWithKeepalive = true; if (this.keptaliveby) (0, timers_1.clearTimeout)(this.keptaliveby); let callback = moreParams.pop(); if (typeof callback !== "function") { if (callback) moreParams.push(callback); callback = null; } params = this.concatParams(params, moreParams); const exec = () => { if (!this.closing) { if (this.keptaliveby) (0, timers_1.clearTimeout)(this.keptaliveby); this.keptaliveby = setTimeout(() => { this.write(params.slice()) .then((data) => { if (typeof callback === "function") callback(null, data); exec(); }) .catch((err) => { if (typeof callback === "function") callback(err, null); exec(); }); }, (this.timeout * 1000) / 2); } }; exec(); } close() { if (this.closing) { if (this.debug) { console.debug("[DEBUG] Close already in progress."); } return Promise.reject(new RosException_1.RosException("ALRDYCLOSNG")); } if (!this.connected) { if (this.debug) { console.debug("[DEBUG] Already disconnected. Skipping close."); } return Promise.resolve(this); } if (this.debug) { console.debug("[DEBUG] Initiating RouterOSAPI close sequence..."); } if (this.connectionHoldInterval) { (0, timers_1.clearTimeout)(this.connectionHoldInterval); if (this.debug) { console.debug("[DEBUG] Cleared connection hold interval."); } } (0, timers_1.clearTimeout)(this.keptaliveby); if (this.debug) { console.debug("[DEBUG] Cleared keepalive timer."); } this.stopAllStreams(); if (this.debug) { console.debug("[DEBUG] Stopped all active streams."); } return new Promise((resolve) => { this.closing = true; if (this.debug) { console.debug("[DEBUG] Waiting for connector to emit 'close'..."); } this.connector.once("close", () => { if (this.debug) { console.debug("[DEBUG] Connector closed. Cleaning up."); } this.connector.destroy(); this.connector = null; this.closing = false; this.connected = false; resolve(this); }); this.connector.close(); }); } openChannel() { this.increaseChannelsOpen(); return new Channel_1.Channel(this.connector); } increaseChannelsOpen() { this.channelsOpen++; } decreaseChannelsOpen() { this.channelsOpen--; } registerStream(stream) { this.registeredStreams.push(stream); } unregisterStream(stream) { this.registeredStreams = this.registeredStreams.filter((s) => s !== stream); } stopAllStreams() { for (const stream of this.registeredStreams) { stream.stop(); } } holdConnection() { if (this.channelsOpen !== 1) return; if (this.connected && !this.holdingConnectionWithKeepalive) { if (this.connectionHoldInterval) (0, timers_1.clearTimeout)(this.connectionHoldInterval); const holdConnInterval = () => { this.connectionHoldInterval = setTimeout(() => { let chann = new Channel_1.Channel(this.connector); chann.on("close", () => { chann = null; }); chann.write(["#"]).then(holdConnInterval).catch(holdConnInterval); }, (this.timeout * 1000) / 2); }; holdConnInterval(); } } releaseConnectionHold() { if (this.channelsOpen > 0) return; if (this.connectionHoldInterval) (0, timers_1.clearTimeout)(this.connectionHoldInterval); } login() { return __awaiter(this, void 0, void 0, function* () { this.connecting = true; info("Sending /login to %s", this.host); const loginTimeoutMs = this.timeout * 1000; return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var _a; const timeout = setTimeout(() => { reject(new RosException_1.RosException("LOGINBLOCKED", { message: "No response to /login. Your IP might not be allowed in /ip service.", })); }, loginTimeoutMs); try { const data = yield this.write("/login", [ `=name=${this.user}`, `=password=${this.password}`, ]); (0, timers_1.clearTimeout)(timeout); if (data.length === 0) { info("Login accepted on %s", this.host); return resolve(this); } if (data.length === 1 && ((_a = data[0]) === null || _a === void 0 ? void 0 : _a.ret)) { const ret = data[0].ret; const challenge = Buffer.alloc(this.password.length + 17); challenge.write(String.fromCharCode(0) + this.password); challenge.write(ret, this.password.length + 1, ret.length / 2, "hex"); const resp = "00" + crypto.createHash("MD5").update(challenge).digest("hex"); const data2 = yield this.write("/login", [ `=name=${this.user}`, `=response=${resp}`, ]); (0, timers_1.clearTimeout)(timeout); return resolve(this); } (0, timers_1.clearTimeout)(timeout); return reject(new RosException_1.RosException("CANTLOGIN")); } catch (err) { (0, timers_1.clearTimeout)(timeout); if (typeof err.message === "string" && err.message.includes("invalid user name or password")) { return reject(new RosException_1.RosException("CANTLOGIN")); } return reject(err); } })); }); } concatParams(firstParameter, parameters) { if (typeof firstParameter === "string") firstParameter = [firstParameter]; for (let parameter of parameters) { if (typeof parameter === "string") parameter = [parameter]; if (parameter.length > 0) firstParameter = firstParameter.concat(parameter); } return firstParameter; } } exports.RouterOSAPI = RouterOSAPI; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUm91dGVyT1NBUEkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvUm91dGVyT1NBUEkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQ0EscURBQWtEO0FBQ2xELHVDQUFvQztBQUNwQyxpREFBOEM7QUFFOUMsdUNBQW9DO0FBQ3BDLGlDQUFpQztBQUNqQywrQkFBK0I7QUFDL0IsbUNBQXNDO0FBQ3RDLG1DQUFzQztBQUd0QyxJQUFJLElBQWtELENBQUM7QUFDdkQsSUFBSSxLQUFtRCxDQUFDO0FBRXhEOztHQUVHO0FBQ0gsTUFBYSxXQUFZLFNBQVEscUJBQVk7SUErQjNDLFlBQVksT0FBb0I7UUFDOUIsS0FBSyxFQUFFLENBQUM7UUFqQlYscUJBQXFCO1FBQ2QsY0FBUyxHQUFZLEtBQUssQ0FBQztRQUNsQyxzQkFBc0I7UUFDZixlQUFVLEdBQVksS0FBSyxDQUFDO1FBQ25DLG1CQUFtQjtRQUNaLFlBQU8sR0FBWSxLQUFLLENBQUM7UUFNeEIsaUJBQVksR0FBVyxDQUFDLENBQUM7UUFDekIsbUNBQThCLEdBQVksS0FBSyxDQUFDO1FBQ2hELDJCQUFzQixHQUEwQixJQUFJLENBQUM7UUFDckQsc0JBQWlCLEdBQWMsRUFBRSxDQUFDO1FBSXhDLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDekIsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUM7SUFDN0IsQ0FBQztJQUVNLFVBQVUsQ0FBQyxPQUFvQjtRQUNwQyxJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFDekIsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQztRQUNqQyxJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxPQUFPLEdBQUcs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