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