Remove fallback CDSI implementation

This commit is contained in:
Alex Bakon 2025-04-16 13:18:30 -04:00 committed by GitHub
commit 514509e2c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 69 additions and 219 deletions

View file

@ -42,7 +42,6 @@ import {
aciSchema,
untaggedPniSchema,
} from '../types/ServiceId';
import type { DirectoryConfigType } from '../types/RendererConfig';
import type { BackupPresentationHeadersType } from '../types/backups';
import * as Bytes from '../Bytes';
import { randomInt } from '../Crypto';
@ -718,7 +717,6 @@ type InitializeOptionsType = {
contentProxyUrl: string;
proxyUrl: string | undefined;
version: string;
directoryConfig: DirectoryConfigType;
disableIPv6: boolean;
};
@ -933,7 +931,6 @@ export type CdsLookupOptionsType = Readonly<{
e164s: ReadonlyArray<string>;
acisAndAccessKeys?: ReadonlyArray<{ aci: AciString; accessKey: string }>;
returnAcisWithoutUaks?: boolean;
useLibsignal?: boolean;
}>;
export type GetGroupCredentialsOptionsType = Readonly<{
@ -1727,7 +1724,6 @@ export function initialize({
storageUrl,
updatesUrl,
resourcesUrl,
directoryConfig,
cdnUrlObject,
certificateAuthority,
contentProxyUrl,
@ -1835,17 +1831,10 @@ export function initialize({
void socketManager.authenticate({ username, password });
}
const { directoryUrl, directoryMRENCLAVE } = directoryConfig;
const cds = new CDSI(libsignalNet, {
logger: log,
proxyUrl,
url: directoryUrl,
mrenclave: directoryMRENCLAVE,
certificateAuthority,
version,
async getAuth() {
return (await _ajax({
call: 'directoryAuthV2',
@ -4727,13 +4716,11 @@ export function initialize({
e164s,
acisAndAccessKeys = [],
returnAcisWithoutUaks,
useLibsignal,
}: CdsLookupOptionsType): Promise<CDSResponseType> {
return cds.request({
e164s,
acisAndAccessKeys,
returnAcisWithoutUaks,
useLibsignal,
});
}
}

View file

@ -1,39 +1,82 @@
// 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 type {
RateLimitedError as NetRateLimitedError,
Net,
} from '@signalapp/libsignal-client';
import {
ErrorCode as LibSignalErrorCode,
LibSignalErrorBase,
} from '@signalapp/libsignal-client';
import pTimeout from 'p-timeout';
import type { CDSBaseOptionsType } from './CDSBase';
import { CDSBase } from './CDSBase';
import type { CDSRequestOptionsType, CDSResponseType } from './Types';
import { sleep } from '../../util/sleep';
import * as durations from '../../util/durations';
import * as Bytes from '../../Bytes';
import { CDSISocket } from './CDSISocket';
import type { CDSSocketManagerBaseOptionsType } from './CDSSocketManagerBase';
import { CDSSocketManagerBase } from './CDSSocketManagerBase';
export type CDSIOptionsType = CDSBaseOptionsType;
export type CDSIOptionsType = Readonly<{
mrenclave: string;
}> &
CDSSocketManagerBaseOptionsType;
const REQUEST_TIMEOUT = 10 * durations.SECOND;
export class CDSI extends CDSSocketManagerBase<CDSISocket, CDSIOptionsType> {
readonly #mrenclave: Buffer;
export class CDSI extends CDSBase<CDSIOptionsType> {
#retryAfter?: number;
constructor(libsignalNet: Net.Net, options: CDSIOptionsType) {
super(libsignalNet, options);
this.#mrenclave = Buffer.from(Bytes.fromHex(options.mrenclave));
constructor(
private readonly libsignalNet: Net.Net,
options: CDSIOptionsType
) {
super(options);
}
protected override getSocketUrl(): string {
const { mrenclave } = this.options;
public async request(
options: CDSRequestOptionsType
): Promise<CDSResponseType> {
const log = this.logger;
return `${this.options.url}/v1/${mrenclave}/discovery`;
}
if (this.#retryAfter !== undefined) {
const delay = Math.max(0, this.#retryAfter - Date.now());
protected override createSocket(socket: WebSocket): CDSISocket {
return new CDSISocket({
logger: this.logger,
socket,
mrenclave: this.#mrenclave,
});
log.info(`CDSSocketManager: waiting ${delay}ms before retrying`);
await sleep(delay);
}
const { acisAndAccessKeys, e164s, returnAcisWithoutUaks = false } = options;
const auth = await this.getAuth();
log.info('CDSSocketManager: making request via libsignal');
try {
log.info('CDSSocketManager: starting lookup request');
const useNewConnectLogic = !window.Signal.RemoteConfig.isEnabled(
'desktop.cdsiViaLibsignal.disableNewConnectionLogic'
);
const { timeout = REQUEST_TIMEOUT } = options;
const response = await pTimeout(
this.libsignalNet.cdsiLookup(auth, {
acisAndAccessKeys,
e164s,
returnAcisWithoutUaks,
useNewConnectLogic,
}),
timeout
);
log.info('CDSSocketManager: lookup request finished');
return response as CDSResponseType;
} catch (error) {
if (
error instanceof LibSignalErrorBase &&
error.code === LibSignalErrorCode.RateLimitedError
) {
const retryError = error as NetRateLimitedError;
this.#retryAfter = Math.max(
this.#retryAfter ?? Date.now(),
Date.now() + retryError.retryAfterSecs * durations.SECOND
);
}
throw error;
}
}
}

View file

@ -1,175 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type {
RateLimitedError as NetRateLimitedError,
Net,
} from '@signalapp/libsignal-client';
import {
ErrorCode as LibSignalErrorCode,
LibSignalErrorBase,
} from '@signalapp/libsignal-client';
import type { connection as WebSocket } from 'websocket';
import pTimeout from 'p-timeout';
import type { AbortableProcess } from '../../util/AbortableProcess';
import * as durations from '../../util/durations';
import { getBasicAuth } from '../../util/getBasicAuth';
import { sleep } from '../../util/sleep';
import { SECOND } from '../../util/durations';
import type { CDSBaseOptionsType } from './CDSBase';
import { CDSBase } from './CDSBase';
import type { CDSSocketBase } from './CDSSocketBase';
import type {
CDSRequestOptionsType,
CDSResponseType,
CDSAuthType,
} from './Types.d';
import { RateLimitedError } from './RateLimitedError';
import { connect as connectWebSocket } from '../WebSocket';
const REQUEST_TIMEOUT = 10 * SECOND;
export type CDSSocketManagerBaseOptionsType = Readonly<{
url: string;
certificateAuthority: string;
version: string;
}> &
CDSBaseOptionsType;
export abstract class CDSSocketManagerBase<
Socket extends CDSSocketBase,
Options extends CDSSocketManagerBaseOptionsType,
> extends CDSBase<Options> {
#retryAfter?: number;
constructor(
private readonly libsignalNet: Net.Net,
options: Options
) {
super(options);
}
public async request(
options: CDSRequestOptionsType
): Promise<CDSResponseType> {
const log = this.logger;
if (this.#retryAfter !== undefined) {
const delay = Math.max(0, this.#retryAfter - Date.now());
log.info(`CDSSocketManager: waiting ${delay}ms before retrying`);
await sleep(delay);
}
if (options.useLibsignal) {
return this.#requestViaLibsignal(options);
}
return this.#requestViaNativeSocket(options);
}
async #requestViaNativeSocket(
options: CDSRequestOptionsType
): Promise<CDSResponseType> {
const log = this.logger;
const auth = await this.getAuth();
log.info('CDSSocketManager: connecting socket');
const socket = await this.#connect(auth).getResult();
log.info('CDSSocketManager: connected socket');
try {
let { timeout = REQUEST_TIMEOUT } = options;
// Handshake
{
const start = Date.now();
await pTimeout(socket.handshake(), timeout);
const duration = Date.now() - start;
timeout = Math.max(timeout - duration, 0);
}
// Send request
const response = await pTimeout(socket.request(options), timeout);
return response;
} catch (error) {
if (error instanceof RateLimitedError) {
if (error.retryAfterSecs > 0) {
this.#retryAfter = Math.max(
this.#retryAfter ?? Date.now(),
Date.now() + error.retryAfterSecs * durations.SECOND
);
}
}
throw error;
} finally {
log.info('CDSSocketManager: closing socket');
void socket.close(3000, 'Normal');
}
}
async #requestViaLibsignal(
options: CDSRequestOptionsType
): Promise<CDSResponseType> {
const log = this.logger;
const { acisAndAccessKeys, e164s, returnAcisWithoutUaks = false } = options;
const auth = await this.getAuth();
log.info('CDSSocketManager: making request via libsignal');
try {
log.info('CDSSocketManager: starting lookup request');
const useNewConnectLogic = !window.Signal.RemoteConfig.isEnabled(
'desktop.cdsiViaLibsignal.disableNewConnectionLogic'
);
const { timeout = REQUEST_TIMEOUT } = options;
const response = await pTimeout(
this.libsignalNet.cdsiLookup(auth, {
acisAndAccessKeys,
e164s,
returnAcisWithoutUaks,
useNewConnectLogic,
}),
timeout
);
log.info('CDSSocketManager: lookup request finished');
return response as CDSResponseType;
} catch (error) {
if (
error instanceof LibSignalErrorBase &&
error.code === LibSignalErrorCode.RateLimitedError
) {
const retryError = error as NetRateLimitedError;
this.#retryAfter = Math.max(
this.#retryAfter ?? Date.now(),
Date.now() + retryError.retryAfterSecs * durations.SECOND
);
}
throw error;
}
}
#connect(auth: CDSAuthType): AbortableProcess<Socket> {
return connectWebSocket<Socket>({
name: 'CDSSocket',
url: this.getSocketUrl(),
version: this.options.version,
proxyAgent: this.proxyAgent,
certificateAuthority: this.options.certificateAuthority,
extraHeaders: {
authorization: getBasicAuth(auth),
},
createResource: (socket: WebSocket): Socket => {
return this.createSocket(socket);
},
});
}
protected abstract getSocketUrl(): string;
protected abstract createSocket(socket: WebSocket): Socket;
}

View file

@ -16,5 +16,4 @@ export type CDSRequestOptionsType = Readonly<{
acisAndAccessKeys: ReadonlyArray<{ aci: AciString; accessKey: string }>;
returnAcisWithoutUaks?: boolean;
timeout?: number;
useLibsignal?: boolean;
}>;

View file

@ -107,9 +107,6 @@ export async function getServiceIdsForE164s(
e164s: expandedE164sArray,
acisAndAccessKeys,
returnAcisWithoutUaks: false,
useLibsignal: window.Signal.RemoteConfig.isEnabled(
'desktop.cdsiViaLibsignal'
),
});
const e164sWithVariantsInCdsi = new Map(

View file

@ -28,7 +28,6 @@ window.WebAPI = window.textsecure.WebAPI.initialize({
storageUrl: config.storageUrl,
updatesUrl: config.updatesUrl,
resourcesUrl: config.resourcesUrl,
directoryConfig: config.directoryConfig,
cdnUrlObject: {
0: config.cdnUrl0,
2: config.cdnUrl2,