UNPKG

renew-ip

Version:

A module for renewing 4G/LTE IP addresses on tethered Android phones from Node JS.

1 lines 9.35 kB
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from \"events\"\n\nimport { getIpData, toggleFlightMode } from \"./api\"\nimport { RenewalFailedError } from \"./errors\"\nimport { IpHistorySchema, RouterConfigSchema } from \"./schemas\"\nimport {\n IpHistory,\n PartialRouterConfig,\n RenewableRouter,\n RouterConfig\n} from \"./types\"\nimport { debug, getLastIp, hasConnection, waitForTimeout } from \"./utils\"\n\n/**\n * Renewable 4G Router\n *\n * @class\n * @classdesc Renews an IP address on a device connected with Automate.\n * @extends {EventEmitter}\n * @implements {RenewableRouter}\n */\nexport class Router extends EventEmitter implements RenewableRouter {\n /**\n * Tracks the successful renewals.\n * @type {Set<IpHistory>}\n * @protected\n */\n protected readonly _history: Set<IpHistory>\n\n /**\n * Tracks the current session renewals.\n * Is cleared at the end of each session.\n *\n * @type {Set<IpHistory>}\n * @protected\n */\n protected readonly _cache: Set<IpHistory>\n\n /**\n * Defines some basic configuration using env file or passed in object.\n * @type {RouterConfig}\n * @protected\n */\n protected readonly _config: RouterConfig\n\n /**\n * Constructor\n * Accepts an optional configuration if not using env file.\n *\n * @param {PartialRouterConfig} config\n */\n public constructor(config?: PartialRouterConfig) {\n super()\n debug(`Constructing router.`)\n // Parse config.\n this._config = RouterConfigSchema.parse(config)\n // Init history.\n this._history = new Set<IpHistory>()\n this._cache = new Set<IpHistory>()\n debug(`Router constructed.`)\n }\n\n /**\n * Readonly accessor for current ip address.\n *\n * @return {string | undefined}\n */\n public get ip(): string | undefined {\n return getLastIp(this._history)\n }\n\n /**\n * Readonly accessor to show current renewal attempts.\n *\n * @return {number}\n */\n public get attempts(): number {\n return this._cache.size\n }\n\n /**\n * Allow users to observe last cached public ip.\n *\n * @return {string | undefined}\n */\n public get lastCachedIp(): string | undefined {\n return getLastIp(this._cache)\n }\n\n /**\n * Public readonly accessor for history as array.\n *\n * @return {IpHistory[]}\n */\n public get history(): IpHistory[] {\n return [...(this._history.values() || [])]\n }\n\n /**\n * Inform user when a new ip has been obtained or drop if dirty address.\n *\n * @return {boolean}\n */\n public validateIp(data: IpHistory | undefined): boolean {\n // Drop if no response.\n if (!data) {\n return false\n }\n // Loop over threat detections.\n for (const [key, value] of Object.entries(data.threat || {})) {\n if (value) {\n debug(`IP flagged as \"${key}\"! Rejecting.`)\n return false\n }\n }\n // Check if ip already used.\n return data.ip !== this.lastCachedIp\n }\n\n /**\n * Return the module to a clean state.\n *\n * @return {Promise<void>}\n */\n public async init(): Promise<void> {\n debug(`Initialising router.`)\n const record: IpHistory = await this.lookup()\n this._history.add(record)\n this._cache.add(record)\n this.emit(\"init\")\n debug(`Initialisation complete.`)\n }\n\n /**\n * Connects to the IPData API and checks the current public ip.\n *\n * @return {Promise<IpHistory>}\n */\n public async lookup(): Promise<IpHistory> {\n debug(`Attempting lookup of public IP address.`)\n const { key, fields } = this._config.ipdata\n return {\n ...(await getIpData({ key, fields })),\n ...{ timestamp: Date.now() }\n }\n }\n\n /**\n * Primary entry point that triggers the renewal.\n *\n * @return {Promise<boolean>}\n */\n public async renew(): Promise<boolean> {\n // Reset the module to a known state.\n debug(`Starting IP renewal cycle.`)\n await this.init()\n // Destructure config items.\n const {\n maxAttempts,\n delay,\n secret,\n to,\n payload,\n device,\n priority\n } = this._config\n // Set initial ip to invalid state.\n let ip: IpHistory | undefined = undefined\n let hasToggled: boolean = false\n // Attempt renewals until a new IP is obtained, or hits max attempts.\n debug(`Attempting renewal with a maximum of ${maxAttempts} attempts.`)\n while (!this.validateIp(ip) && this.attempts < maxAttempts) {\n if (!hasToggled) {\n // Send payload to Automate to trigger the flow on the device.\n debug(`Attempting to toggle flight mode remotely.`)\n await toggleFlightMode({ secret, to, payload, device, priority })\n hasToggled = true\n }\n // Only attempt an ip check if device has connectivity.\n if (await hasConnection()) {\n debug(`Interface has connectivity!`)\n // Check host's public ip address.\n ip = IpHistorySchema.parse(await this.lookup())\n debug(`Reflected IP context: `, ip)\n // Add the current public ip to cache.\n this._cache.add(ip)\n // If new IP is different to last ip, return a success response.\n if (this.validateIp(ip)) {\n // Add to the history set to register new public ip.\n this._history.add(ip)\n this.emit(\"success\", ip)\n return true\n } else {\n // Received invalid IP, try for a new IP.\n this._cache.clear()\n hasToggled = false\n }\n }\n // Wait for a specified delay between connection checks.\n debug(`Connection not online, waiting ${delay} ms.`)\n await waitForTimeout(delay)\n }\n // Renewal process failed, return a negative response.\n this._cache.clear()\n debug(`Exceeded maximum attempts of ${maxAttempts}.`)\n this.emit(\"error\", new RenewalFailedError(maxAttempts))\n return false\n }\n}\n"]}