From e58e29e63e287d40e90737e43353fa6dcec6be9c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Ortiz karliatto Date: Sun, 1 Dec 2024 10:53:37 +0100 Subject: [PATCH] wip: works !!! --- packages/request-manager/src/controller.ts | 42 +++-- .../request-manager/src/controllerExternal.ts | 14 ++ packages/request-manager/src/index.ts | 1 + packages/request-manager/src/utils.ts | 1 + packages/suite-desktop-core/src/index.d.ts | 4 +- .../src/libs/processes/BaseProcess.ts | 2 + .../src/libs/processes/TorExternalProcess.ts | 44 ++--- packages/suite-desktop-core/src/libs/store.ts | 2 + .../suite-desktop-core/src/modules/tor.ts | 164 +++++++++++++----- .../src/modules/trezor-connect.ts | 8 + 10 files changed, 194 insertions(+), 88 deletions(-) create mode 100644 packages/request-manager/src/controllerExternal.ts diff --git a/packages/request-manager/src/controller.ts b/packages/request-manager/src/controller.ts index 7829e1c257f4..97382e25b9a8 100644 --- a/packages/request-manager/src/controller.ts +++ b/packages/request-manager/src/controller.ts @@ -68,6 +68,24 @@ export class TorController extends EventEmitter { }, BOOTSTRAP_SLOW_TRESHOLD); } + private onMessageReceived(message: string) { + const bootstrap: BootstrapEvent[] = bootstrapParser(message); + bootstrap.forEach(event => { + if (event.type !== 'progress') return; + if (event.progress && !this.getIsBootstrapping()) { + // We consider that bootstrap has started when we receive any bootstrap event and + // Tor is not bootstrapping yet. + // If we do not receive any bootstrapping event, we can consider there is something going wrong and + // an error will be thrown when `MAX_TRIES_WAITING` is reached in `waitUntilAlive`. + this.startBootstrap(); + } + if (event.progress === BOOTSTRAP_EVENT_PROGRESS.Done) { + this.successfullyBootstrapped(); + } + this.emit('bootstrap/event', event); + }); + } + public async getTorConfiguration( processId: number, snowflakeBinaryPath?: string, @@ -174,24 +192,6 @@ export class TorController extends EventEmitter { return config; } - public onMessageReceived(message: string) { - const bootstrap: BootstrapEvent[] = bootstrapParser(message); - bootstrap.forEach(event => { - if (event.type !== 'progress') return; - if (event.progress && !this.getIsBootstrapping()) { - // We consider that bootstrap has started when we receive any bootstrap event and - // Tor is not bootstrapping yet. - // If we do not receive any bootstrapping event, we can consider there is something going wrong and - // an error will be thrown when `MAX_TRIES_WAITING` is reached in `waitUntilAlive`. - this.startBootstrap(); - } - if (event.progress === BOOTSTRAP_EVENT_PROGRESS.Done) { - this.successfullyBootstrapped(); - } - this.emit('bootstrap/event', event); - }); - } - public async waitUntilAlive(): Promise { this.status = TOR_CONTROLLER_STATUS.Bootstrapping; await waitUntil( @@ -199,9 +199,13 @@ export class TorController extends EventEmitter { WAITING_TIME, async () => { const isConnected = await this.controlPort.connect(); + console.log('isConnected', isConnected); const isAlive = this.controlPort.ping(); + console.log('isAlive', isAlive); + const isCircuitEstablished = this.getIsCircuitEstablished(); + console.log('isCircuitEstablished', isCircuitEstablished); // It is running so let's not wait anymore. - if (isConnected && isAlive && this.getIsCircuitEstablished()) { + if (isConnected && isAlive && isCircuitEstablished) { return true; } else { return false; diff --git a/packages/request-manager/src/controllerExternal.ts b/packages/request-manager/src/controllerExternal.ts new file mode 100644 index 000000000000..47c483a25b44 --- /dev/null +++ b/packages/request-manager/src/controllerExternal.ts @@ -0,0 +1,14 @@ +import { EventEmitter } from 'events'; + +export class TorControllerExternal extends EventEmitter { + constructor() { + super(); + } + getTorConfiguration() { + return ''; + } + waitUntilAlive() {} + getStatus() {} + closeActiveCircuits() {} + stop() {} +} diff --git a/packages/request-manager/src/index.ts b/packages/request-manager/src/index.ts index aef9257d3391..2e198e5fe49c 100644 --- a/packages/request-manager/src/index.ts +++ b/packages/request-manager/src/index.ts @@ -1,4 +1,5 @@ export { TorController } from './controller'; +export { TorControllerExternal } from './controllerExternal'; export { createInterceptor } from './interceptor'; export type { InterceptedEvent, BootstrapEvent, TorControllerStatus } from './types'; export { TOR_CONTROLLER_STATUS } from './types'; diff --git a/packages/request-manager/src/utils.ts b/packages/request-manager/src/utils.ts index 423de9cf6dd9..3a2691666af4 100644 --- a/packages/request-manager/src/utils.ts +++ b/packages/request-manager/src/utils.ts @@ -18,6 +18,7 @@ export const waitUntil = ( } try { const completed = await checkToSuccess(); + console.log('completed', completed); if (completed) { return; } diff --git a/packages/suite-desktop-core/src/index.d.ts b/packages/suite-desktop-core/src/index.d.ts index 1429b9693c57..9ca5edd1d4cb 100644 --- a/packages/suite-desktop-core/src/index.d.ts +++ b/packages/suite-desktop-core/src/index.d.ts @@ -101,8 +101,8 @@ declare type TorSettings = { running: boolean; // Tor should be enabled host: string; // Hostname of the tor process through which traffic is routed port: number; // Port of the Tor process through which traffic is routed - // controlPort: number; // Port of the Tor Control Port - // torDataDir: string; // Path of tor data directory + controlPort: number; // Port of the Tor Control Port + torDataDir: string; // Path of tor data directory snowflakeBinaryPath: string; // Path in user system to the snowflake binary // externalPort: number; // Port of the Tor process run by the user externally to suite useExternalTor: boolean; // Tor should use external daemon instead of the one built-in suite. diff --git a/packages/suite-desktop-core/src/libs/processes/BaseProcess.ts b/packages/suite-desktop-core/src/libs/processes/BaseProcess.ts index 4110a22a2d56..3fa5e070bb8d 100644 --- a/packages/suite-desktop-core/src/libs/processes/BaseProcess.ts +++ b/packages/suite-desktop-core/src/libs/processes/BaseProcess.ts @@ -74,6 +74,7 @@ export abstract class BaseProcess { * @param params Command line parameters for the process */ async start(params: string[] = []) { + console.log('start in base process'); if (this.startupThrottle) { this.logger.warn(this.logTopic, 'Canceling process start (throttle)'); @@ -82,6 +83,7 @@ export abstract class BaseProcess { const status = await this.status(); + console.log('status in base process', status); // Service is running, nothing to do if (status.service) { this.logger.warn(this.logTopic, 'Canceling process start (service running)'); diff --git a/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts b/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts index 55fc849fae41..db2862f28c0a 100644 --- a/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts +++ b/packages/suite-desktop-core/src/libs/processes/TorExternalProcess.ts @@ -1,8 +1,10 @@ import { TorConnectionOptions } from '@trezor/request-manager/src/types'; import { checkSocks5Proxy } from '@trezor/node-utils'; -import { Status } from './BaseProcess'; +import { BaseProcess, Status } from './BaseProcess'; import { waitUntil } from '@trezor/request-manager/src/utils'; +import { TorControllerExternal } from '@trezor/request-manager'; +// import { TorController } from '@trezor/request-manager'; export type TorProcessStatus = Status & { isBootstrapping?: boolean; isSocks5ProxyPort?: boolean }; @@ -12,30 +14,13 @@ const DEFAULT_TOR_EXTERNAL_HOST = '127.0.0.1'; const DEFAULT_TOR_EXTERNAL_PORT = 9050; export class TorExternalProcess { - // torController: TorController; - // port: number; - // controlPort: number; - // torHost: string; - // torDataDir: string; - // snowflakeBinaryPath: string; - // useExternalTor: boolean; - - stop = true; - - constructor(_options: TorConnectionOptions & { useExternalTor: boolean }) { - // this.port = options.port; - // this.controlPort = options.controlPort; - // this.torHost = options.host; - // this.torDataDir = options.torDataDir; - // this.useExternalTor = options.useExternalTor; - // this.snowflakeBinaryPath = ''; - // this.torController = new TorController({ - // host: this.torHost, - // port: this.port, - // controlPort: this.controlPort, - // torDataDir: this.torDataDir, - // snowflakeBinaryPath: this.snowflakeBinaryPath, - // }); + isStopped = true; + torController: TorControllerExternal; + port = DEFAULT_TOR_EXTERNAL_PORT; + host = DEFAULT_TOR_EXTERNAL_HOST; + constructor() { + // this.torController = new FakeTorController(); + this.torController = new TorControllerExternal(); } setTorConfig(_torConfig: { useExternalTor: boolean; snowflakeBinaryPath: string }) { @@ -64,7 +49,7 @@ export class TorExternalProcess { } public getIsStopped() { - return this.stop; + return this.isStopped; } public async waitUntilAliveExternal(host: string, port: number): Promise { @@ -89,7 +74,12 @@ export class TorExternalProcess { async start(): Promise { console.log('startExternal in TorProcess start'); + this.isStopped = false; await this.waitUntilAliveExternal(DEFAULT_TOR_EXTERNAL_HOST, DEFAULT_TOR_EXTERNAL_PORT); - this.stop = false; + } + + async stop() { + // We should not stop External Tor Process but ignore it. + this.isStopped = true; } } diff --git a/packages/suite-desktop-core/src/libs/store.ts b/packages/suite-desktop-core/src/libs/store.ts index 419567b74870..67700c2b90f0 100644 --- a/packages/suite-desktop-core/src/libs/store.ts +++ b/packages/suite-desktop-core/src/libs/store.ts @@ -63,9 +63,11 @@ export class Store { return this.store.get('torSettings', { running: false, port: 9050, + controlPort: 9051, host: '127.0.0.1', snowflakeBinaryPath: '', useExternalTor: false, + torDataDir: '', }); } diff --git a/packages/suite-desktop-core/src/modules/tor.ts b/packages/suite-desktop-core/src/modules/tor.ts index 2efd910e08e7..d4e6f9a038a7 100644 --- a/packages/suite-desktop-core/src/modules/tor.ts +++ b/packages/suite-desktop-core/src/modules/tor.ts @@ -12,28 +12,52 @@ import { getFreePort } from '@trezor/node-utils'; import { validateIpcMessage } from '@trezor/ipc-proxy'; import { TorProcess, TorProcessStatus } from '../libs/processes/TorProcess'; +import { TorExternalProcess } from '../libs/processes/TorExternalProcess'; import { app, ipcMain } from '../typed-electron'; import type { Dependencies } from './index'; const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) => { const { logger } = global; - const host = '127.0.0.1'; - const port = await getFreePort(); - const controlPort = await getFreePort(); - const torDataDir = path.join(app.getPath('userData'), 'tor'); const initialSettings = store.getTorSettings(); - store.setTorSettings({ ...initialSettings, host, port }); - - const tor = new TorProcess({ - host, - port, - controlPort, - torDataDir, - snowflakeBinaryPath: initialSettings.snowflakeBinaryPath, + store.setTorSettings({ + ...initialSettings, + port: await getFreePort(), + controlPort: await getFreePort(), + torDataDir: path.join(app.getPath('userData'), 'tor'), }); + const settings = store.getTorSettings(); + console.log('settings in createTorProcess module/tor.ts', settings); + + const implementations = [ + { + type: 'tor', + impl: new TorProcess({ + host: settings.host, + port: settings.port, + controlPort: settings.controlPort, + torDataDir: settings.torDataDir, + snowflakeBinaryPath: settings.snowflakeBinaryPath, + }), + }, + { + type: 'tor-external', + impl: new TorExternalProcess(), + }, + ]; + + const getTarget = () => { + const { useExternalTor } = store.getTorSettings(); + const currentTarget = useExternalTor ? 'tor-external' : 'tor'; + return implementations.find(impl => impl.type === currentTarget)!.impl; + }; + + const updateTorPort = (port: number) => { + store.setTorSettings({ ...store.getTorSettings(), port }); + }; + const setProxy = (rule: string) => { logger.info('tor', `Setting proxy rules to "${rule}"`); // Including network session of electron auto-updater in the Tor proxy. @@ -44,10 +68,17 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) }); }; - const getProxySettings = (shouldEnableTor: boolean) => - shouldEnableTor ? { proxy: `socks://${host}:${port}` } : { proxy: '' }; - - const handleTorProcessStatus = (status: TorProcessStatus) => { + const getProxySettings = (shouldEnableTor: boolean) => { + const { useExternalTor, port, host } = store.getTorSettings(); + return shouldEnableTor + ? { + proxy: `socks://${host}:${useExternalTor ? 9050 : port}`, + } + : { proxy: '' }; + }; + const handleTorProcessStatus = (status: TorProcessStatus, shouldRunTor: boolean) => { + console.log('handleTorProcessStatus'); + console.log('status', status); let type: TorStatus; if (!status.process) { @@ -59,6 +90,12 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) } else { type = TorStatus.Disabled; } + + if (shouldRunTor && status.isSocks5ProxyPort && store.getTorSettings().useExternalTor) { + type = TorStatus.Enabled; + } + + console.log('type', type); mainThreadEmitter.emit('module/tor-status-update', type); mainWindowProxy.getInstance()?.webContents.send('tor/status', { type, @@ -90,20 +127,58 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) } }; - const setupTor = async (shouldEnableTor: boolean) => { - const isTorRunning = (await tor.status()).process; - const { snowflakeBinaryPath } = store.getTorSettings(); + const createFakeBootstrapProcess = () => { + let progress = 0; + const duration = 3 * 1000; // 3 seconds. + const interval = 300; // update progress every 300ms + + const increment = (100 / duration) * interval; + const intervalId = setInterval(() => { + progress += increment; + if (progress >= 100) { + progress = 100; + clearInterval(intervalId); + } + handleBootstrapEvent({ + type: 'progress', + progress: `${progress}`, + summary: 'Using External Tor fake progress', + }); + }, interval); + }; + const setupTor = async (shouldEnableTor: boolean) => { + console.log('setupTor', setupTor); + const isTorRunning = (await getTarget().status()).process; + console.log('isTorRunning', isTorRunning); + const { snowflakeBinaryPath, useExternalTor } = store.getTorSettings(); + console.log('useExternalTor', useExternalTor); + + console.log('shouldEnableTor', shouldEnableTor); + console.log('isTorRunning', isTorRunning); if (shouldEnableTor === isTorRunning) { return; } if (shouldEnableTor === true) { - setProxy(`socks5://${host}:${port}`); - tor.torController.on('bootstrap/event', handleBootstrapEvent); + // TODO: maybe instead of hardcode port get it from tor.getPort(); ??? + const { useExternalTor, port, host } = store.getTorSettings(); + const proxyRule = `socks5://${host}:${useExternalTor ? 9050 : port}`; + console.log('proxyRule', proxyRule); + setProxy(proxyRule); + getTarget().torController.on('bootstrap/event', handleBootstrapEvent); try { - tor.setTorConfig({ snowflakeBinaryPath }); - await tor.start(); + const { useExternalTor } = store.getTorSettings(); + console.log('useExternalTor in setupTor', useExternalTor); + getTarget().setTorConfig({ snowflakeBinaryPath, useExternalTor }); + // TODO: maybe instead of hardcode port get it from tor.getPort(); ??? + updateTorPort(useExternalTor ? 9050 : settings.port); + if (useExternalTor) { + await getTarget().start(); + createFakeBootstrapProcess(); + } else { + await getTarget().start(); + } } catch (error) { mainWindowProxy.getInstance()?.webContents.send('tor/bootstrap', { type: 'error', @@ -111,18 +186,19 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) }); // When there is error does not mean that the process is stop, // so we make sure to stop it so we are able to restart it. - tor.stop(); + getTarget().stop(); throw error; } finally { - tor.torController.removeAllListeners(); + getTarget().torController.removeAllListeners(); } } else { + console.log('we are sending disable, is it right???'); mainWindowProxy.getInstance()?.webContents.send('tor/status', { type: TorStatus.Disabling, }); setProxy(''); - tor.torController.stop(); - await tor.stop(); + getTarget().torController.stop(); + await getTarget().stop(); } store.setTorSettings({ ...store.getTorSettings(), running: shouldEnableTor }); @@ -141,9 +217,7 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) try { store.setTorSettings({ - running: store.getTorSettings().running, - host, - port, + ...store.getTorSettings(), snowflakeBinaryPath, useExternalTor, }); @@ -181,6 +255,7 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) // This is only required when 'toggle' because when app starts with TOR enable TrezorConnect is // correctly set in module trezor-connect-ipc. const proxySettings = getProxySettings(shouldEnableTor); + console.log('proxySettings', proxySettings); await TrezorConnect.setProxy(proxySettings); @@ -192,6 +267,7 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) await setupTor(!shouldEnableTor); const proxySettings = getProxySettings(!shouldEnableTor); + console.log('proxySettings in error', proxySettings); await TrezorConnect.setProxy(proxySettings); @@ -208,8 +284,10 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) } // Once Tor is toggled it renderer should know the new status. - const status = await tor.status(); - handleTorProcessStatus(status); + let status = await getTarget().status(); + console.log('status in tor/toggle at the end', status); + + handleTorProcessStatus(status, shouldEnableTor); return { success: true }; }); @@ -218,11 +296,15 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) let lastCircuitResetTime = 0; const socksTimeout = 30000; // this value reflects --SocksTimeout flag set by TorController config mainThreadEmitter.on('module/reset-tor-circuits', event => { + if (store.getTorSettings().useExternalTor) { + logger.debug('tor', `Ignore circuit reset. Running External Tor without Control Port.`); + return; + } const lastResetDiff = Date.now() - lastCircuitResetTime; if (lastResetDiff > socksTimeout) { logger.debug('tor', `Close active circuits. Triggered by identity ${event.identity}`); lastCircuitResetTime = Date.now(); - tor.torController.closeActiveCircuits(); + getTarget().torController.closeActiveCircuits(); } else { logger.debug( 'tor', @@ -232,9 +314,11 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) }); ipcMain.on('tor/get-status', async () => { - logger.debug('tor', `Getting status (${store.getTorSettings().running ? 'ON' : 'OFF'})`); - const status = await tor.status(); - handleTorProcessStatus(status); + const { running } = store.getTorSettings(); + logger.debug('tor', `Getting status (${running ? 'ON' : 'OFF'})`); + let status = await getTarget().status(); + console.log('status in tor/get-status', status); + handleTorProcessStatus(status, running); }); if (app.commandLine.hasSwitch('tor')) { @@ -242,7 +326,7 @@ const load = async ({ mainWindowProxy, store, mainThreadEmitter }: Dependencies) store.setTorSettings({ ...store.getTorSettings(), running: true }); } - return tor; + return getTarget; }; type TorModule = (dependencies: Dependencies) => { @@ -252,13 +336,13 @@ type TorModule = (dependencies: Dependencies) => { export const init: TorModule = dependencies => { let loaded = false; - let tor: TorProcess | undefined; + let getTarget: any; const onLoad = async () => { if (loaded) return { shouldRunTor: false }; loaded = true; - tor = await load(dependencies); + getTarget = await load(dependencies); const torSettings = dependencies.store.getTorSettings(); return { @@ -269,7 +353,7 @@ export const init: TorModule = dependencies => { const onQuit = async () => { const { logger } = global; logger.info('tor', 'Stopping (app quit)'); - await tor?.stop(); + await getTarget()?.stop(); }; return { onLoad, onQuit }; diff --git a/packages/suite-desktop-core/src/modules/trezor-connect.ts b/packages/suite-desktop-core/src/modules/trezor-connect.ts index eb8c1d375cf3..9ecc10cf5604 100644 --- a/packages/suite-desktop-core/src/modules/trezor-connect.ts +++ b/packages/suite-desktop-core/src/modules/trezor-connect.ts @@ -12,9 +12,17 @@ export const initBackground: ModuleInitBackground = ({ mainThreadEmitter, store logger.info(SERVICE_NAME, `Starting service`); const setProxy = (ifRunning = false) => { + console.log('SETPROXY IN INIT BACKGROUND'); + console.log('SETPROXY IN INIT BACKGROUND'); + console.log('SETPROXY IN INIT BACKGROUND'); + console.log('SETPROXY IN INIT BACKGROUND'); + console.log('SETPROXY IN INIT BACKGROUND'); + console.log('SETPROXY IN INIT BACKGROUND'); const tor = store.getTorSettings(); + console.log('tor', tor); if (ifRunning && !tor.running) return Promise.resolve(); const payload = tor.running ? { proxy: `socks://${tor.host}:${tor.port}` } : { proxy: '' }; + console.log('payload for initBackground', payload); logger.info(SERVICE_NAME, `${tor.running ? 'Enable' : 'Disable'} proxy ${payload.proxy}`); return TrezorConnect.setProxy(payload);