UNPKG

@fibercom/routeros-api

Version:

Robust MikroTik RouterOS API client for Node.js and TypeScript

416 lines 33.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(); /** Host to connect */ this.host = ""; /** Username to use */ this.user = ""; /** Password of the username */ this.password = ""; /** Port of the API */ this.port = 8728; /** Timeout of the connection */ this.timeout = 10; /** Debug mode enabled */ this.debug = false; /** Connected flag */ this.connected = false; /** Connecting flag */ this.connecting = false; /** Closing flag */ this.closing = false; /** Keep connection alive */ this.keepalive = false; this.connector = null; this.channelsOpen = 0; this.holdingConnectionWithKeepalive = false; this.connectionHoldInterval = null; this.registeredStreams = []; this.setOptions(options); this.initializeDebuggers(); } setOptions(options) { var _a, _b, _c, _d, _e, _f; this.host = options.host; this.user = (_a = options.user) !== null && _a !== void 0 ? _a : ""; this.password = (_b = options.password) !== null && _b !== void 0 ? _b : ""; this.port = (_c = options.port) !== null && _c !== void 0 ? _c : (options.tls ? 8729 : 8728); this.timeout = (_d = options.timeout) !== null && _d !== void 0 ? _d : 10; this.tls = options.tls; this.keepalive = (_e = options.keepalive) !== null && _e !== void 0 ? _e : false; this.debug = (_f = options.debug) !== null && _f !== void 0 ? _f : 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) { const message = err instanceof Error ? err.message : String(err); console.warn(`[WARN] Connection attempt ${tries} failed: ${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, }); 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); }); this.connector.once("close", () => { this.connected = false; this.connecting = false; }); try { yield this.connector.connect(); // ✅ await critical! yield this.login(); this.connected = true; this.connecting = false; 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); const chann = this.openChannel(); this.holdConnection(); chann.once("close", () => { 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() { return __awaiter(this, void 0, void 0, function* () { 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."); } yield this.stopAllStreams(); if (this.debug) { console.debug("[DEBUG] Stopped all active streams."); } const connector = this.connector; if (!connector) { this.connected = false; return this; } return new Promise((resolve) => { this.closing = true; if (this.debug) { console.debug("[DEBUG] Waiting for connector to emit 'close'..."); } connector.once("close", () => { if (this.debug) { console.debug("[DEBUG] Connector closed. Cleaning up."); } connector.destroy(); if (this.connector === connector) this.connector = null; this.closing = false; this.connected = false; resolve(this); }); connector.close(); }); }); } openChannel() { if (!this.connector) throw new RosException_1.RosException("ENOTCONN"); 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() { return __awaiter(this, void 0, void 0, function* () { yield Promise.allSettled(this.registeredStreams.map((stream) => 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(() => { const connector = this.connector; if (!connector) return; let chann = new Channel_1.Channel(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 (err instanceof Error && 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUm91dGVyT1NBUEkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvUm91dGVyT1NBUEkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7O0FBQ0EscURBQWtEO0FBQ2xELHVDQUFvQztBQUNwQyxpREFBOEM7QUFFOUMsdUNBQW9DO0FBQ3BDLGlDQUFpQztBQUNqQywrQkFBZ0M7QUFDaEMsbUNBQXNDO0FBQ3RDLG1DQUFzQztBQUd0QyxJQUFJLElBQWtELENBQUM7QUFDdkQsSUFBSSxLQUFtRCxDQUFDO0FBRXhEOztHQUVHO0FBQ0gsTUFBYSxXQUFZLFNBQVEscUJBQVk7SUErQjNDLFlBQVksT0FBb0I7UUFDOUIsS0FBSyxFQUFFLENBQUM7UUEvQlYsc0JBQXNCO1FBQ2YsU0FBSSxHQUFXLEVBQUUsQ0FBQztRQUN6QixzQkFBc0I7UUFDZixTQUFJLEdBQVcsRUFBRSxDQUFDO1FBQ3pCLCtCQUErQjtRQUN4QixhQUFRLEdBQVcsRUFBRSxDQUFDO1FBQzdCLHNCQUFzQjtRQUNmLFNBQUksR0FBVyxJQUFJLENBQUM7UUFDM0IsZ0NBQWdDO1FBQ3pCLFlBQU8sR0FBVyxFQUFFLENBQUM7UUFHNUIseUJBQXlCO1FBQ2xCLFVBQUssR0FBWSxLQUFLLENBQUM7UUFDOUIscUJBQXFCO1FBQ2QsY0FBUyxHQUFZLEtBQUssQ0FBQztRQUNsQyxzQkFBc0I7UUFDZixlQUFVLEdBQVksS0FBSyxDQUFDO1FBQ25DLG1CQUFtQjtRQUNaLFlBQU8sR0FBWSxLQUFLLENBQUM7UUFDaEMsNEJBQTRCO1FBQ3JCLGNBQVMsR0FBWSxLQUFLLENBQUM7UUFFMUIsY0FBUyxHQUFxQixJQUFJLENBQUM7UUFFbkMsaUJBQVksR0FBVyxDQUFDLENBQUM7UUFDekIsbUNBQThCLEdBQVksS0FBSyxDQUFDO1FBQ2hELDJCQUFzQixHQUEwQixJQUFJLENBQUM7UUFDckQsc0JBQWlCLEdBQWMsRUFBRSxDQUFDO1FBSXhDLElBQUksQ0FBQyxVQUFVLENBQUMsT0FB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