diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index 41eb29390..77cbff4c3 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -4343,7 +4343,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see ``` -## attest 0.1.0, device-transfer 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-core 0.1.0, libsignal-ffi 0.42.0, libsignal-jni 0.42.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, libsignal-node 0.42.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, poksho 0.7.0, signal-crypto 0.1.0, signal-media 0.1.0, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, signal-pin 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0 +## attest 0.1.0, device-transfer 0.1.0, libsignal-bridge 0.1.0, libsignal-bridge-macros 0.1.0, libsignal-core 0.1.0, libsignal-ffi 0.44.0, libsignal-jni 0.44.0, libsignal-message-backup 0.1.0, libsignal-message-backup-macros 0.1.0, libsignal-net 0.1.0, libsignal-node 0.44.0, libsignal-protocol 0.1.0, libsignal-svr3 0.1.0, poksho 0.7.0, signal-crypto 0.1.0, signal-media 0.1.0, signal-neon-futures 0.1.0, signal-neon-futures-tests 0.1.0, signal-pin 0.1.0, usernames 0.1.0, zkcredential 0.1.0, zkgroup 0.9.0 ``` GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/app/main.ts b/app/main.ts index abbae45e8..21b9b4dd5 100644 --- a/app/main.ts +++ b/app/main.ts @@ -2426,6 +2426,9 @@ ipc.on('get-config', async event => { preferredSystemLocales: getPreferredSystemLocales(), localeOverride: getLocaleOverride(), version: app.getVersion(), + libsignalNetEnvironment: config.has('libsignalNetEnvironment') + ? config.get('libsignalNetEnvironment') + : undefined, buildCreation: config.get('buildCreation'), buildExpiration: config.get('buildExpiration'), challengeUrl: config.get('challengeUrl'), diff --git a/package.json b/package.json index 9fc53ec05..5ea14ace7 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "@react-aria/utils": "3.16.0", "@react-spring/web": "9.5.5", "@signalapp/better-sqlite3": "8.7.1", - "@signalapp/libsignal-client": "0.42.0", + "@signalapp/libsignal-client": "0.44.0", "@signalapp/ringrtc": "2.39.3", "@signalapp/windows-dummy-keystroke": "1.0.0", "@types/fabric": "4.5.3", diff --git a/ts/textsecure/SocketManager.ts b/ts/textsecure/SocketManager.ts index dd97f3fee..cb86508bc 100644 --- a/ts/textsecure/SocketManager.ts +++ b/ts/textsecure/SocketManager.ts @@ -1,6 +1,7 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +import type { Net } from '@signalapp/libsignal-client'; import URL from 'url'; import type { RequestInit, Response } from 'node-fetch'; import { Headers } from 'node-fetch'; @@ -103,7 +104,10 @@ export class SocketManager extends EventListener { private reconnectController: AbortController | undefined; - constructor(private readonly options: SocketManagerOptions) { + constructor( + private readonly libsignalNet: Net.Net, + private readonly options: SocketManagerOptions + ) { super(); this.hasStoriesDisabled = options.hasStoriesDisabled; @@ -565,14 +569,9 @@ export class SocketManager extends EventListener { } private connectLibsignalUnauthenticated(): AbortableProcess { - return new AbortableProcess( - `WebSocket.connect(libsignal.${UNAUTHENTICATED_CHANNEL_NAME})`, - { - abort() { - // noop - }, - }, - Promise.resolve(new LibsignalWebSocketResource(this.options.version)) + return LibsignalWebSocketResource.connect( + this.libsignalNet, + UNAUTHENTICATED_CHANNEL_NAME ); } @@ -666,7 +665,8 @@ export class SocketManager extends EventListener { const url = `${this.options.url}${path}?${qs.encode(queryWithDefaults)}`; const { version } = this.options; - return connectWebSocket({ + const start = performance.now(); + const webSocketResourceConnection = connectWebSocket({ name, url, version, @@ -675,17 +675,69 @@ export class SocketManager extends EventListener { extraHeaders, - createResource(socket: WebSocket): IWebSocketResource { - return !resourceOptions.transportOption || - resourceOptions.transportOption === TransportOption.Original - ? new WebSocketResource(socket, resourceOptions) - : new WebSocketResourceWithShadowing( - socket, - resourceOptions, - version - ); + createResource(socket: WebSocket): WebSocketResource { + const duration = (performance.now() - start).toFixed(1); + log.info( + `WebSocketResource(${resourceOptions.name}) connected in ${duration}ms` + ); + return new WebSocketResource(socket, resourceOptions); }, }); + + const shadowingModeEnabled = + !resourceOptions.transportOption || + resourceOptions.transportOption === TransportOption.Original; + return shadowingModeEnabled + ? webSocketResourceConnection + : this.connectWithShadowing(webSocketResourceConnection, resourceOptions); + } + + /** + * A method that takes in an `AbortableProcess<>` that establishes + * a `WebSocketResource` connection and wraps it in a process + * that also tries to establish a `LibsignalWebSocketResource` connection. + * + * The shadowing connection will not block the main one (e.g. if it takes + * longer to connect) and an error in the shadowing connection will not + * affect the overall behavior. + * + * @param mainConnection an `AbortableProcess` responsible + * for establishing a Desktop system WebSocket connection. + * @param options `WebSocketResourceOptions` options + * @private + */ + private connectWithShadowing( + mainConnection: AbortableProcess, + options: WebSocketResourceOptions + ): AbortableProcess { + // creating an `AbortableProcess` of libsignal websocket connection + const shadowingConnection = LibsignalWebSocketResource.connect( + this.libsignalNet, + options.name + ); + const shadowWrapper = async () => { + // if main connection results in an error, + // it's propagated as the error of the resulting process + const mainSocket = await mainConnection.resultPromise; + // here, we're not awaiting on `shadowingConnection.resultPromise` + // and just letting `WebSocketResourceWithShadowing` + // initiate and handle the result of the shadowing connection attempt + return new WebSocketResourceWithShadowing( + mainSocket, + shadowingConnection, + options + ); + }; + return new AbortableProcess( + `WebSocketResourceWithShadowing.connect(${options.name})`, + { + abort() { + mainConnection.abort(); + shadowingConnection.abort(); + }, + }, + shadowWrapper() + ); } private async checkResource( diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index cc4c78beb..ac70f81be 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -16,6 +16,7 @@ import { z } from 'zod'; import type { Readable } from 'stream'; import type { connection as WebSocket } from 'websocket'; +import { Net } from '@signalapp/libsignal-client'; import { assertDev, strictAssert } from '../util/assert'; import { isRecord } from '../util/isRecord'; import * as durations from '../util/durations'; @@ -71,6 +72,7 @@ import * as log from '../logging/log'; import { maybeParseUrl, urlPathFromComponents } from '../util/url'; import { SECOND } from '../util/durations'; import type { IWebSocketResource } from './WebsocketResources'; +import { Environment, getEnvironment } from '../environment'; // Note: this will break some code that expects to be able to use err.response when a // web request fails, because it will force it to text. But it is very useful for @@ -78,6 +80,32 @@ import type { IWebSocketResource } from './WebsocketResources'; const DEBUG = false; const DEFAULT_TIMEOUT = 30 * SECOND; +// Libsignal has internally configured values for domain names +// (and other connectivity params) of the services. +function resolveLibsignalNetEnvironment( + appEnv: Environment, + libsignalNetEnv: string | undefined +): Net.Environment { + switch (appEnv) { + case Environment.Production: + return Net.Environment.Production; + case Environment.Development: + // In the case of the `Development` Desktop env, + // we should be checking the provided string value + // of `libsignalNetEnv` + switch (libsignalNetEnv) { + case 'production': + return Net.Environment.Production; + default: + return Net.Environment.Staging; + } + case Environment.Test: + case Environment.Staging: + default: + return Net.Environment.Staging; + } +} + function _createRedactor( ...toReplace: ReadonlyArray ): RedactUrl { @@ -588,6 +616,7 @@ type InitializeOptionsType = { proxyUrl: string | undefined; version: string; directoryConfig: DirectoryConfigType; + libsignalNetEnvironment: string | undefined; }; export type MessageType = Readonly<{ @@ -1249,6 +1278,7 @@ export function initialize({ contentProxyUrl, proxyUrl, version, + libsignalNetEnvironment, }: InitializeOptionsType): WebAPIConnectType { if (!isString(url)) { throw new Error('WebAPI.initialize: Invalid server url'); @@ -1290,6 +1320,17 @@ export function initialize({ throw new Error('WebAPI.initialize: Invalid version'); } + // `libsignalNet` is an instance of a class from libsignal that is responsible + // for providing network layer API and related functionality. + // It's important to have a single instance of this class as it holds + // resources that are shared across all other use cases. + const env = resolveLibsignalNetEnvironment( + getEnvironment(), + libsignalNetEnvironment + ); + log.info(`libsignal net environment resolved to [${Net.Environment[env]}]`); + const libsignalNet = new Net.Net(env); + // Thanks to function-hoisting, we can put this return statement before all of the // below function definitions. return { @@ -1313,7 +1354,7 @@ export function initialize({ let activeRegistration: ExplodePromiseResultType | undefined; - const socketManager = new SocketManager({ + const socketManager = new SocketManager(libsignalNet, { url, artCreatorUrl, certificateAuthority, @@ -1344,7 +1385,7 @@ export function initialize({ const { directoryUrl, directoryMRENCLAVE } = directoryConfig; - const cds = new CDSI({ + const cds = new CDSI(libsignalNet, { logger: log, proxyUrl, diff --git a/ts/textsecure/WebSocket.ts b/ts/textsecure/WebSocket.ts index fc5dfa1a7..23f1c4a03 100644 --- a/ts/textsecure/WebSocket.ts +++ b/ts/textsecure/WebSocket.ts @@ -17,6 +17,7 @@ import { ConnectTimeoutError, HTTPError } from './Errors'; import { handleStatusCode, translateError } from './Utils'; const TEN_SECONDS = 10 * durations.SECOND; +const WEBSOCKET_CONNECT_TIMEOUT = TEN_SECONDS; const KEEPALIVE_INTERVAL_MS = TEN_SECONDS; export type IResource = { @@ -42,7 +43,7 @@ export function connect({ version, proxyAgent, extraHeaders = {}, - timeout = TEN_SECONDS, + timeout = WEBSOCKET_CONNECT_TIMEOUT, createResource, }: ConnectOptionsType): AbortableProcess { const fixedScheme = url diff --git a/ts/textsecure/WebsocketResources.ts b/ts/textsecure/WebsocketResources.ts index 5c685d85f..087ca7026 100644 --- a/ts/textsecure/WebsocketResources.ts +++ b/ts/textsecure/WebsocketResources.ts @@ -26,7 +26,6 @@ /* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/brace-style */ -import { Net } from '@signalapp/libsignal-client'; import type { connection as WebSocket, IMessage } from 'websocket'; import Long from 'long'; import pTimeout from 'p-timeout'; @@ -35,8 +34,9 @@ import net from 'net'; import { z } from 'zod'; import { clearInterval } from 'timers'; import { random } from 'lodash'; -import type { DebugInfo } from '@signalapp/libsignal-client/Native'; +import type { ChatServiceDebugInfo } from '@signalapp/libsignal-client/Native'; +import type { Net } from '@signalapp/libsignal-client'; import type { EventHandler } from './EventTarget'; import EventTarget from './EventTarget'; @@ -50,14 +50,13 @@ import { SignalService as Proto } from '../protobuf'; import * as log from '../logging/log'; import * as Timers from '../Timers'; import type { IResource } from './WebSocket'; -import { isProduction, isStaging } from '../util/version'; +import { isProduction } from '../util/version'; import { ToastType } from '../types/Toast'; +import { AbortableProcess } from '../util/AbortableProcess'; const THIRTY_SECONDS = 30 * durations.SECOND; -const HEALTHCHECK_TIMEOUT = durations.SECOND; - const STATS_UPDATE_INTERVAL = durations.MINUTE; const MAX_MESSAGE_SIZE = 512 * 1024; @@ -83,6 +82,7 @@ export namespace IpVersion { } const AggregatedStatsSchema = z.object({ + connectionFailures: z.number(), requestsCompared: z.number(), ipVersionMismatches: z.number(), unexpectedReconnects: z.number(), @@ -127,6 +127,7 @@ export namespace AggregatedStats { export function add(a: AggregatedStats, b: AggregatedStats): AggregatedStats { return { requestsCompared: a.requestsCompared + b.requestsCompared, + connectionFailures: a.connectionFailures + b.connectionFailures, healthcheckFailures: a.healthcheckFailures + b.healthcheckFailures, ipVersionMismatches: a.ipVersionMismatches + b.ipVersionMismatches, unexpectedReconnects: a.unexpectedReconnects + b.unexpectedReconnects, @@ -138,6 +139,7 @@ export namespace AggregatedStats { export function createEmpty(): AggregatedStats { return { requestsCompared: 0, + connectionFailures: 0, ipVersionMismatches: 0, unexpectedReconnects: 0, healthcheckFailures: 0, @@ -152,9 +154,10 @@ export namespace AggregatedStats { return false; } return ( - stats.healthcheckBadStatus + stats.healthcheckFailures > 20 || - stats.ipVersionMismatches > 50 || - stats.unexpectedReconnects > 50 + stats.healthcheckBadStatus + + stats.healthcheckFailures + + stats.connectionFailures > + 20 || stats.unexpectedReconnects > 50 ); } @@ -261,11 +264,42 @@ export interface IWebSocketResource extends IResource { } export class LibsignalWebSocketResource implements IWebSocketResource { - private readonly net: Net.Net; + constructor( + private readonly chatService: Net.ChatService, + private readonly socketIpVersion: IpVersion | undefined + ) {} - constructor(version: string) { - this.net = new Net.Net( - isStaging(version) ? Net.Environment.Staging : Net.Environment.Production + public static connect( + libsignalNet: Net.Net, + name: string + ): AbortableProcess { + const chatService = libsignalNet.newChatService(); + const connectAsync = async () => { + try { + const debugInfo = await chatService.connectUnauthenticated(); + log.info(`LibsignalWebSocketResource(${name}) connected`, debugInfo); + return new LibsignalWebSocketResource( + chatService, + IpVersion.fromDebugInfoCode(debugInfo.ipType) + ); + } catch (error) { + // Handle any errors that occur during connection + log.error( + `LibsignalWebSocketResource(${name}) connection failed`, + Errors.toLogFormat(error) + ); + throw error; + } + }; + return new AbortableProcess( + `LibsignalWebSocketResource.connect(${name})`, + { + abort() { + // if interrupted, trying to disconnect + drop(chatService.disconnect()); + }, + }, + connectAsync() ); } @@ -273,6 +307,10 @@ export class LibsignalWebSocketResource implements IWebSocketResource { return undefined; } + public ipVersion(): IpVersion | undefined { + return this.socketIpVersion; + } + public addEventListener( _name: 'close', _handler: (ev: CloseEvent) => void @@ -281,13 +319,11 @@ export class LibsignalWebSocketResource implements IWebSocketResource { } public close(_code?: number, _reason?: string): void { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.net.disconnectChatService(); + drop(this.chatService.disconnect()); } public shutdown(): void { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.net.disconnectChatService(); + drop(this.chatService.disconnect()); } public forceKeepAlive(): void { @@ -301,16 +337,15 @@ export class LibsignalWebSocketResource implements IWebSocketResource { public async sendRequestGetDebugInfo( options: SendRequestOptions - ): Promise<[Response, DebugInfo]> { - const { response, debugInfo } = await this.net.unauthenticatedFetchAndDebug( - { + ): Promise<[Response, ChatServiceDebugInfo]> { + const { response, debugInfo } = + await this.chatService.unauthenticatedFetchAndDebug({ verb: options.verb, path: options.path, headers: options.headers ? options.headers : [], body: options.body, timeoutMillis: options.timeout, - } - ); + }); return [ new Response(response.body, { status: response.status, @@ -323,9 +358,7 @@ export class LibsignalWebSocketResource implements IWebSocketResource { } export class WebSocketResourceWithShadowing implements IWebSocketResource { - private main: WebSocketResource; - - private shadowing: LibsignalWebSocketResource; + private shadowing: LibsignalWebSocketResource | undefined; private stats: AggregatedStats; @@ -336,12 +369,10 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource { private logId: string; constructor( - socket: WebSocket, - options: WebSocketResourceOptions, - version: string + private readonly main: WebSocketResource, + private readonly shadowingConnection: AbortableProcess, + options: WebSocketResourceOptions ) { - this.main = new WebSocketResource(socket, options); - this.shadowing = new LibsignalWebSocketResource(version); this.stats = AggregatedStats.createEmpty(); this.logId = `WebSocketResourceWithShadowing(${options.name})`; this.statsTimer = setInterval( @@ -351,6 +382,28 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource { this.shadowingWithReporting = options.transportOption === TransportOption.ShadowingHigh; + // the idea is that we want to keep the shadowing connection process + // "in the background", so that the main connection wouldn't need to wait on it. + // then when we're connected, `this.shadowing` socket resource is initialized + // or an error reported in case of connection failure + const initializeAfterConnected = async () => { + try { + this.shadowing = await shadowingConnection.resultPromise; + // checking IP one time per connection + if (this.main.ipVersion() !== this.shadowing.ipVersion()) { + this.stats.ipVersionMismatches += 1; + const mainIpType = this.main.ipVersion(); + const shadowIpType = this.shadowing.ipVersion(); + log.warn( + `${this.logId}: libsignal websocket IP [${shadowIpType}], Desktop websocket IP [${mainIpType}]` + ); + } + } catch (error) { + this.stats.connectionFailures += 1; + } + }; + drop(initializeAfterConnected()); + this.addEventListener('close', (_ev): void => { clearInterval(this.statsTimer); this.updateStats(options.name); @@ -392,12 +445,20 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource { public close(): void { this.main.close(); - this.shadowing.close(); + if (this.shadowing) { + this.shadowing.close(); + } else { + this.shadowingConnection.abort(); + } } public shutdown(): void { this.main.shutdown(); - this.shadowing.shutdown(); + if (this.shadowing) { + this.shadowing.shutdown(); + } else { + this.shadowingConnection.abort(); + } } public forceKeepAlive(timeout?: number): void { @@ -421,12 +482,20 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource { } private async sendShadowRequest(): Promise { + // it could be that we're still connecting libsignal websocket + // in which case we're skipping the check + if (!this.shadowing) { + log.info( + `${this.logId}: skipping healthcheck - websocket not connected yet` + ); + return; + } try { const [healthCheckResult, debugInfo] = await this.shadowing.sendRequestGetDebugInfo({ verb: 'GET', path: '/v1/keepalive', - timeout: HEALTHCHECK_TIMEOUT, + timeout: KEEPALIVE_TIMEOUT_MS, }); this.stats.requestsCompared += 1; if (!isSuccessfulStatusCode(healthCheckResult.status)) { @@ -435,18 +504,7 @@ export class WebSocketResourceWithShadowing implements IWebSocketResource { `${this.logId}: keepalive via libsignal responded with status [${healthCheckResult.status}]` ); } - const ipVersion = IpVersion.fromDebugInfoCode(debugInfo.ipType); - if (this.main.ipVersion() !== ipVersion) { - this.stats.ipVersionMismatches += 1; - log.warn( - `${ - this.logId - }: keepalive via libsignal using IP [${ipVersion}] while main is using IP [${this.main.ipVersion()}]` - ); - } - if (debugInfo.reconnectCount > 1) { - this.stats.unexpectedReconnects = debugInfo.reconnectCount - 1; - } + this.stats.unexpectedReconnects = debugInfo.reconnectCount; } catch (error) { this.stats.healthcheckFailures += 1; log.warn( @@ -823,7 +881,7 @@ const KEEPALIVE_INTERVAL_MS = 30 * durations.SECOND; // immediate disconnect. const STALE_THRESHOLD_MS = 5 * durations.MINUTE; -// If we don't receive a response to keepalive request within 10 seconds - +// If we don't receive a response to keepalive request within 30 seconds - // close the socket. const KEEPALIVE_TIMEOUT_MS = 30 * durations.SECOND; diff --git a/ts/textsecure/cds/CDSI.ts b/ts/textsecure/cds/CDSI.ts index 2c471229b..821470b3a 100644 --- a/ts/textsecure/cds/CDSI.ts +++ b/ts/textsecure/cds/CDSI.ts @@ -1,6 +1,7 @@ // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only +import type { Net } from '@signalapp/libsignal-client'; import type { connection as WebSocket } from 'websocket'; import * as Bytes from '../../Bytes'; @@ -16,8 +17,8 @@ export type CDSIOptionsType = Readonly<{ export class CDSI extends CDSSocketManagerBase { private readonly mrenclave: Buffer; - constructor(options: CDSIOptionsType) { - super(options); + constructor(libsignalNet: Net.Net, options: CDSIOptionsType) { + super(libsignalNet, options); this.mrenclave = Buffer.from(Bytes.fromHex(options.mrenclave)); } diff --git a/ts/textsecure/cds/CDSSocketManagerBase.ts b/ts/textsecure/cds/CDSSocketManagerBase.ts index 4d506247d..d7e68debb 100644 --- a/ts/textsecure/cds/CDSSocketManagerBase.ts +++ b/ts/textsecure/cds/CDSSocketManagerBase.ts @@ -1,9 +1,11 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { RateLimitedError as NetRateLimitedError } from '@signalapp/libsignal-client'; -import { +import type { + RateLimitedError as NetRateLimitedError, Net, +} from '@signalapp/libsignal-client'; +import { ErrorCode as LibSignalErrorCode, LibSignalErrorBase, } from '@signalapp/libsignal-client'; @@ -25,7 +27,6 @@ import type { } from './Types.d'; import { RateLimitedError } from './RateLimitedError'; import { connect as connectWebSocket } from '../WebSocket'; -import { Environment, getEnvironment } from '../../environment'; const REQUEST_TIMEOUT = 10 * SECOND; @@ -42,6 +43,10 @@ export abstract class CDSSocketManagerBase< > extends CDSBase { private retryAfter?: number; + constructor(private readonly libsignalNet: Net.Net, options: Options) { + super(options); + } + public async request( options: CDSRequestOptionsType ): Promise { @@ -106,24 +111,22 @@ export abstract class CDSSocketManagerBase< options: CDSRequestOptionsType ): Promise { const log = this.logger; - const { - acisAndAccessKeys, - e164s, - timeout = REQUEST_TIMEOUT, - returnAcisWithoutUaks = false, - } = options; + const { acisAndAccessKeys, e164s, returnAcisWithoutUaks = false } = options; const auth = await this.getAuth(); log.info('CDSSocketManager: making request via libsignal'); - const net = new Net.Net(this.libsignalNetEnvironment()); try { log.info('CDSSocketManager: starting lookup request'); - const response = await net.cdsiLookup(auth, { - acisAndAccessKeys, - e164s, - timeout, - returnAcisWithoutUaks, - }); + + const { timeout = REQUEST_TIMEOUT } = options; + const response = await pTimeout( + this.libsignalNet.cdsiLookup(auth, { + acisAndAccessKeys, + e164s, + returnAcisWithoutUaks, + }), + timeout + ); log.info('CDSSocketManager: lookup request finished'); return response as CDSResponseType; @@ -142,19 +145,6 @@ export abstract class CDSSocketManagerBase< } } - private libsignalNetEnvironment(): Net.Environment { - const env = getEnvironment(); - switch (env) { - case Environment.Production: - return Net.Environment.Production; - case Environment.Development: - case Environment.Test: - case Environment.Staging: - default: - return Net.Environment.Staging; - } - } - private connect(auth: CDSAuthType): AbortableProcess { return connectWebSocket({ name: 'CDSSocket', diff --git a/ts/types/RendererConfig.ts b/ts/types/RendererConfig.ts index 648b355db..09497a11c 100644 --- a/ts/types/RendererConfig.ts +++ b/ts/types/RendererConfig.ts @@ -71,6 +71,7 @@ export const rendererConfigSchema = z.object({ resourcesUrl: configRequiredStringSchema, userDataPath: configRequiredStringSchema, version: configRequiredStringSchema, + libsignalNetEnvironment: configOptionalStringSchema, directoryConfig: directoryConfigSchema, // Only used by main window diff --git a/ts/windows/main/phase1-ipc.ts b/ts/windows/main/phase1-ipc.ts index f7ec2f758..d0b4f4fc1 100644 --- a/ts/windows/main/phase1-ipc.ts +++ b/ts/windows/main/phase1-ipc.ts @@ -175,6 +175,7 @@ if (config.ciMode !== 'full' && config.environment !== 'test') { type NetworkStatistics = { signalConnectionCount?: string; + unauthorizedConnectionFailures?: string; unauthorizedRequestsCompared?: string; unauthorizedHealthcheckFailures?: string; unauthorizedHealthcheckBadStatus?: string; @@ -206,6 +207,9 @@ ipc.on('additional-log-data-request', async event => { if (unauthorizedStats.requestsCompared > 0) { networkStatistics = { ...networkStatistics, + unauthorizedConnectionFailures: formatCountForLogging( + unauthorizedStats.connectionFailures + ), unauthorizedRequestsCompared: formatCountForLogging( unauthorizedStats.requestsCompared ), diff --git a/ts/windows/main/phase2-dependencies.ts b/ts/windows/main/phase2-dependencies.ts index 33b14fb6f..5bc4c8162 100644 --- a/ts/windows/main/phase2-dependencies.ts +++ b/ts/windows/main/phase2-dependencies.ts @@ -38,6 +38,7 @@ window.WebAPI = window.textsecure.WebAPI.initialize({ contentProxyUrl: config.contentProxyUrl, proxyUrl: config.proxyUrl, version: config.version, + libsignalNetEnvironment: config.libsignalNetEnvironment, }); window.libphonenumberInstance = PhoneNumberUtil.getInstance(); diff --git a/yarn.lock b/yarn.lock index 12faa63d7..460a17cc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3983,7 +3983,16 @@ bindings "^1.5.0" tar "^6.1.0" -"@signalapp/libsignal-client@0.42.0", "@signalapp/libsignal-client@^0.42.0": +"@signalapp/libsignal-client@0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.44.0.tgz#c08bf33bb16276baff35a932b1f43d19d17028cf" + integrity sha512-tbxkRoNd1l2cg6aN5tyiTCy724qU/OlKpX9IECbMu/W1V77SaQS6ch0kPdR+KClBMyEtWxnd/q3/V4NMiENF4w== + dependencies: + node-gyp-build "^4.2.3" + type-fest "^3.5.0" + uuid "^8.3.0" + +"@signalapp/libsignal-client@^0.42.0": version "0.42.0" resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.42.0.tgz#259d87233f1e065ae93cf8fe758bcc2461e3e814" integrity sha512-03lr1LmMTSy3lto8lbdaQMvuvwqs7+fatNP3Kp6dHAnR/OoXh6Y1l493U5X86Z87XGdM0gfGntxZwZ+Qju9Dpg== @@ -7568,7 +7577,7 @@ caniuse-lite@^1.0.30001349, caniuse-lite@^1.0.30001541: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz" integrity sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw== -canvas@^2.6.1, "canvas@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz": +canvas@^2.6.1, "canvas@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz", dmg-license@^1.0.11, "dmg-license@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz", jsdom@^15.2.1, "jsdom@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz": version "1.0.0" resolved "https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz#cb46cf7e01574aa6390858149f66897afe53c9ca" @@ -8946,10 +8955,6 @@ dmg-builder@24.6.3: optionalDependencies: dmg-license "^1.0.11" -dmg-license@^1.0.11, "dmg-license@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz": - version "1.0.0" - resolved "https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz#cb46cf7e01574aa6390858149f66897afe53c9ca" - dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -13378,10 +13383,6 @@ jsdoc@^4.0.0: strip-json-comments "^3.1.0" underscore "~1.13.2" -jsdom@^15.2.1, "jsdom@https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz": - version "1.0.0" - resolved "https://registry.yarnpkg.com/nop/-/nop-1.0.0.tgz#cb46cf7e01574aa6390858149f66897afe53c9ca" - jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" @@ -18452,7 +18453,7 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -18495,15 +18496,6 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -18584,7 +18576,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -18618,13 +18610,6 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -20278,7 +20263,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -20312,15 +20297,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"