signal-desktop/ts/util/dns.ts

116 lines
2.8 KiB
TypeScript
Raw Normal View History

// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { LookupOneOptions, LookupAllOptions, LookupAddress } from 'dns';
import { lookup as nodeLookup } from 'dns';
import { ipcRenderer, net } from 'electron';
import type { ResolvedHost } from 'electron';
import { strictAssert } from './assert';
import { drop } from './drop';
2023-04-05 21:49:33 +00:00
const HOST_ALLOWLIST = new Set([
// Production
'chat.signal.org',
'storage.signal.org',
'cdsi.signal.org',
'cdn.signal.org',
'cdn2.signal.org',
'create.signal.art',
// Staging
'chat.staging.signal.org',
'storage-staging.signal.org',
'cdsi.staging.signal.org',
'cdn-staging.signal.org',
'cdn2-staging.signal.org',
'create.staging.signal.art',
// Common
'updates2.signal.org',
'sfu.voip.signal.org',
]);
function lookupAll(
hostname: string,
opts: LookupOneOptions | LookupAllOptions,
callback: (
err: NodeJS.ErrnoException | null,
addresses: string | Array<LookupAddress>,
family?: number
) => void
): void {
if (!HOST_ALLOWLIST.has(hostname)) {
nodeLookup(hostname, opts, callback);
return;
}
// Node.js support various signatures, but we only support one.
strictAssert(typeof opts === 'object', 'missing options');
strictAssert(typeof callback === 'function', 'missing callback');
async function run() {
let result: ResolvedHost;
try {
let queryType: 'A' | 'AAAA' | undefined;
if (opts.family === 4) {
queryType = 'A';
} else if (opts.family === 6) {
queryType = 'AAAA';
}
2023-04-05 21:49:33 +00:00
if (net) {
// Main process
result = await net.resolveHost(hostname, {
queryType,
});
} else {
// Renderer
result = await ipcRenderer.invoke(
'net.resolveHost',
hostname,
queryType
);
}
const addresses = result.endpoints.map(({ address, family }) => {
let numericFamily = -1;
if (family === 'ipv4') {
numericFamily = 4;
} else if (family === 'ipv6') {
numericFamily = 6;
}
return {
address,
family: numericFamily,
};
});
if (!opts.all) {
2023-06-05 19:55:09 +00:00
const random = addresses.at(
Math.floor(Math.random() * addresses.length)
);
if (random === undefined) {
callback(
new Error(`Hostname: ${hostname} cannot be resolved`),
'',
-1
);
return;
}
callback(null, random.address, random.family);
return;
}
2023-06-05 19:55:09 +00:00
callback(null, addresses);
} catch (error) {
callback(error, []);
}
}
drop(run());
}
// Note: `nodeLookup` has a complicated type due to compatibility requirements.
export const electronLookup = lookupAll as typeof nodeLookup;