diff --git a/ts/services/calling.ts b/ts/services/calling.ts index 8ce0962d4..3ba10910a 100644 --- a/ts/services/calling.ts +++ b/ts/services/calling.ts @@ -156,6 +156,8 @@ const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map< const CLEAN_EXPIRED_GROUP_CALL_RINGS_INTERVAL = 10 * durations.MINUTE; +const ICE_SERVER_IS_IP_LIKE = /(turn|turns|stun):[.\d]+/; + // We send group call update messages to tell other clients to peek, which triggers // notifications, timeline messages, big green "Join" buttons, and so on. This enum // represents the three possible states we can be in. This helps ensure that we don't @@ -315,6 +317,8 @@ export class CallingClass { public _sfuUrl?: string; + public _iceServerOverride?: string; + private lastMediaDeviceSettings?: MediaDeviceSettings; private deviceReselectionTimer?: NodeJS.Timeout; @@ -2598,23 +2602,36 @@ export class CallingClass { // If the peer is not in the user's system contacts, force IP hiding. const isContactUntrusted = !isInSystemContacts(conversation.attributes); + // only include hostname with urlsWithIps + const iceServers = this._iceServerOverride + ? [ + { + hostname: ICE_SERVER_IS_IP_LIKE.test(this._iceServerOverride) + ? iceServer.hostname + : '', + username: iceServer.username, + password: iceServer.password, + urls: [this._iceServerOverride.toString()], + }, + ] + : // proritize ice servers with IPs to avoid DNS + [ + { + hostname: iceServer.hostname, + username: iceServer.username, + password: iceServer.password, + urls: (iceServer.urlsWithIps ?? []).slice(), + }, + { + hostname: '', + username: iceServer.username, + password: iceServer.password, + urls: (iceServer.urls ?? []).slice(), + }, + ]; + const callSettings = { - // only include hostname with urlsWithIps - // proritize ice servers with IPs to avoid DNS - iceServers: [ - { - hostname: iceServer.hostname, - username: iceServer.username, - password: iceServer.password, - urls: (iceServer.urlsWithIps ?? []).slice(), - }, - { - hostname: '', - username: iceServer.username, - password: iceServer.password, - urls: (iceServer.urls ?? []).slice(), - }, - ], + iceServers, hideIp: shouldRelayCalls || isContactUntrusted, dataMode: DataMode.Normal, // TODO: DESKTOP-3101 diff --git a/ts/window.d.ts b/ts/window.d.ts index 6a690732f..01c197569 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -195,6 +195,7 @@ declare global { getServerPublicParams: () => string; getGenericServerPublicParams: () => string; getSfuUrl: () => string; + getIceServerOverride: () => string; getSocketStatus: () => SocketStatus; getSyncRequest: (timeoutMillis?: number) => SyncRequest; getTitle: () => string; diff --git a/ts/windows/main/start.ts b/ts/windows/main/start.ts index 44e5dcfa3..789160b7f 100644 --- a/ts/windows/main/start.ts +++ b/ts/windows/main/start.ts @@ -55,6 +55,8 @@ if (!isProduction(window.SignalContext.getVersion())) { window.MessageCache.__DEPRECATED$getById(id), getReduxState: () => window.reduxStore.getState(), getSfuUrl: () => window.Signal.Services.calling._sfuUrl, + getIceServerOverride: () => + window.Signal.Services.calling._iceServerOverride, getStorageItem: (name: keyof StorageAccessType) => window.storage.get(name), putStorageItem: ( name: K, @@ -69,6 +71,14 @@ if (!isProduction(window.SignalContext.getVersion())) { setSfuUrl: (url: string) => { window.Signal.Services.calling._sfuUrl = url; }, + setIceServerOverride: (url: string) => { + if (!/(turn|turns|stun):.*]/.test(url)) { + log.warn( + 'Override url should be prefixed with `turn:`, `turns:`, or `stun:` else override may not work' + ); + } + window.Signal.Services.calling._iceServerOverride = url; + }, sqlCall: (name: string, ...args: ReadonlyArray) => ipcInvoke(name, args), ...(window.SignalContext.config.ciMode === 'benchmark'