"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // index.ts var src_exports = {}; __export(src_exports, { AlcatelClient: () => AlcatelClient }); module.exports = __toCommonJS(src_exports); var import_node_crypto = require("node:crypto"); var import_node_net = require("node:net"); var import_constants = require("./constants.cjs"); var import_verbs = require("./verbs.cjs"); var AlcatelClient = class { _hostName; _userName; _hashedPassword; _publicEncryptionKey; _restrictedEncryptionKey; _tclVerificationKey; __sequence = 1; __token; constructor(hostName, password = "", options = {}) { const { userName = "admin", publicEncryptionKey = "EE5GRouter2020", tclVerificationKey = "KSDHSDFOGQ5WERYTUIQWERTYUISDFG1HJZXCVCXBN2GDSMNDHKVKFsVBNf", pbkdf2Salt = "e5dl12XYVggihggafXWf0f2YSf2Xngd1", pbkdf2Rounds = 1024, pbkdf2KeyLength = 64, pbkdf2Algorithm = "sha1" } = options; this._hostName = hostName; this._userName = userName; this._tclVerificationKey = tclVerificationKey; this._publicEncryptionKey = publicEncryptionKey; this._restrictedEncryptionKey = (0, import_node_crypto.pbkdf2Sync)( password, pbkdf2Salt, pbkdf2Rounds, pbkdf2KeyLength, pbkdf2Algorithm ).toString("hex"); const len = this._restrictedEncryptionKey.length; const left = this._restrictedEncryptionKey.substring(0, len / 2); const right = this._restrictedEncryptionKey.substring(len / 2, len); this._hashedPassword = right + left; for (const verb of import_verbs.publicVerbs) { const name = verb[0].toLowerCase() + verb.substring(1); Object.defineProperty(this, name, { value: () => this._post(verb, this._publicEncryptionKey, {}) }); } for (const verb of [...import_verbs.restrictedVerbs, "connect", "disconnect"]) { const name = verb[0].toLowerCase() + verb.substring(1); Object.defineProperty(this, name, { value: async () => { try { if (!this.__token) await this.login(); return await this._post(verb, this._restrictedEncryptionKey, {}, this.__token); } catch (error) { if (!["401", "402", "403"].includes(error.code)) throw error; await this.login(); return this._post(verb, this._restrictedEncryptionKey, {}, this.__token); } } }); } } async _post(verb, encryptionKey, parameters, token) { const url = `http://${this._hostName}/rpc?name=${verb}`; const headers = { "_TclRequestVerificationKey": this._tclVerificationKey }; if (token) headers["Authorization"] = `token=${token}`; const request = { id: `${++this.__sequence}.0`, jsonrpc: "2.0", method: verb, params: encrypt(parameters, encryptionKey) }; const body = JSON.stringify(request); const response = await fetch(url, { method: "POST", body, headers }); if (response.status !== 200) { throw new Error(`HTTP error (status=${response.status})`); } const result = await response.json(); if (result.jsonrpc !== "2.0") { throw new Error(`Wrong RPC response version (jsonrpc=${request.jsonrpc}`); } if (result.id != request.id) { throw new Error(`RPC ID mismatch (request=${request.id}, response=${result.id}`); } if (result.error) { const error = new Error(`RPC Error: ${result.error.message} (code=${result.error.code})`); error.code = result.error.code; throw error; } if (!result.result) throw new Error("RPC Error: No result"); return decrypt(result.result, encryptionKey); } async login() { const result = await this._post("Login", this._restrictedEncryptionKey, { UserName: this._userName, Password: this._hashedPassword, ClientType: 0 }); if (!result.token) throw new Error("No token in response"); this.__token = result.token; } /** Poll basic status (does **not** require login). */ async pollBasic() { const [systemInfo, systemStatus] = await Promise.all([ this.getSystemInfo(), this.getSystemStatus() ]); return { imei: systemInfo["IMEI"] || "", iccid: systemInfo["ICCID"] || "", device: systemInfo["DeviceName"] || "", connection_status: import_constants.CONNECTION_STATUSES[systemStatus["ConnectionStatus"]] || "Unknown", network_type: import_constants.NETWORK_TYPES[systemStatus["NetworkType"]] || "Unknown", network_name: systemStatus["NetworkName"] || null, strength: systemStatus["SignalStrength"] || 0 }; } /** Poll extended status (**does require** login). */ async pollExtended() { const systemInfo = await this.getSystemInfo(); const networkInfo = await this.getNetworkInfo(); const connectionState = await this.getConnectionState(); return { imei: systemInfo["IMEI"] || "", iccid: systemInfo["ICCID"] || "", device: systemInfo["DeviceName"] || "", connection_status: import_constants.CONNECTION_STATUSES[connectionState["ConnectionStatus"]] || "Unknown", bytes_in: connectionState["DlBytes"] || 0, bytes_out: connectionState["UlBytes"] || 0, bytes_in_rate: connectionState["DlRate"] || 0, bytes_out_rate: connectionState["UlRate"] || 0, ipv4_addr: (0, import_node_net.isIPv4)(connectionState["IPv4Adrress"]) ? connectionState["IPv4Adrress"] : null, ipv6_addr: (0, import_node_net.isIPv6)(connectionState["IPv6Adrress"]) ? connectionState["IPv6Adrress"] : null, network_name: networkInfo["NetworkName"] || null, network_type: import_constants.NETWORK_TYPES[networkInfo["NetworkType"]] || "Unknown", strength: networkInfo["SignalStrength"] || 0, rssi: parseInt(networkInfo["RSSI"]) || -Infinity, rsrp: parseInt(networkInfo["RSRP"]) || -Infinity, sinr: parseInt(networkInfo["SINR"]) || Infinity, rsrq: parseInt(networkInfo["RSRQ"]) || Infinity }; } /** * Poll extended status (**does require** login). * * @deprecated Use {@link pollBasic()} or {@link pollExtended}. */ /* coverage ignore next */ poll() { return this.pollExtended(); } }; function kdf(xpassword, salt, keyLen, ivLen) { const password = Buffer.from(xpassword, "binary"); if (!salt) salt = (0, import_node_crypto.randomBytes)(8); if (salt.length !== 8) throw new RangeError("SALT must be 8 bytes long"); const key = Buffer.alloc(keyLen); const iv = Buffer.alloc(ivLen); let tmp = Buffer.alloc(0); while (keyLen > 0 || ivLen > 0) { const hash = (0, import_node_crypto.createHash)("md5"); hash.update(tmp); hash.update(password); hash.update(salt); tmp = hash.digest(); let used = 0; if (keyLen > 0) { const keyStart = key.length - keyLen; used = Math.min(keyLen, tmp.length); tmp.copy(key, keyStart, 0, used); keyLen -= used; } if (used < tmp.length && ivLen > 0) { const ivStart = iv.length - ivLen; const length = Math.min(ivLen, tmp.length - used); tmp.copy(iv, ivStart, used, used + length); ivLen -= length; } } return { key, iv, salt }; } function encrypt(parameters, secret) { const { key, iv, salt } = kdf(secret, null, 32, 16); const cipher = (0, import_node_crypto.createCipheriv)("aes-256-cbc", key, iv); const encrypted1 = cipher.update(JSON.stringify(parameters)); const encrypted2 = cipher.final(); const result = Buffer.concat([Buffer.from("Salted__"), salt, encrypted1, encrypted2]); return result.toString("base64"); } function decrypt(message, secret) { const token = Buffer.from(message, "base64"); const prefix = token.subarray(0, 8).toString("latin1"); const salt = token.subarray(8, 16); const data = token.subarray(16); if (prefix !== "Salted__") throw new Error("Invalid message (not salted)"); const { key, iv } = kdf(secret, salt, 32, 16); const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-cbc", key, iv); const decrypted1 = decipher.update(data); const decrypted2 = decipher.final(); const decrypted = Buffer.concat([decrypted1, decrypted2]).toString("utf-8"); return JSON.parse(decrypted); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AlcatelClient }); //# sourceMappingURL=index.cjs.map