2023-03-22 00:43:55 +00:00
|
|
|
// Copyright 2023 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2023-05-30 23:57:16 +00:00
|
|
|
import type { LookupOneOptions, LookupAllOptions, LookupAddress } from 'dns';
|
|
|
|
import { lookup as nodeLookup } from 'dns';
|
|
|
|
import { ipcRenderer, net } from 'electron';
|
|
|
|
import type { ResolvedHost } from 'electron';
|
2023-03-22 00:43:55 +00:00
|
|
|
|
|
|
|
import { strictAssert } from './assert';
|
2023-05-30 23:57:16 +00:00
|
|
|
import { drop } from './drop';
|
2023-03-22 00:43:55 +00:00
|
|
|
|
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',
|
|
|
|
]);
|
|
|
|
|
2023-05-30 23:57:16 +00:00
|
|
|
function lookupAll(
|
2023-03-22 00:43:55 +00:00
|
|
|
hostname: string,
|
2023-05-30 23:57:16 +00:00
|
|
|
opts: LookupOneOptions | LookupAllOptions,
|
2023-03-22 00:43:55 +00:00
|
|
|
callback: (
|
|
|
|
err: NodeJS.ErrnoException | null,
|
2023-05-30 23:57:16 +00:00
|
|
|
addresses: string | Array<LookupAddress>,
|
|
|
|
family?: number
|
2023-03-22 00:43:55 +00:00
|
|
|
) => void
|
|
|
|
): void {
|
2023-05-30 23:57:16 +00:00
|
|
|
if (!HOST_ALLOWLIST.has(hostname)) {
|
|
|
|
nodeLookup(hostname, opts, callback);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-22 00:43:55 +00:00
|
|
|
// Node.js support various signatures, but we only support one.
|
|
|
|
strictAssert(typeof opts === 'object', 'missing options');
|
|
|
|
strictAssert(typeof callback === 'function', 'missing callback');
|
|
|
|
|
2023-05-30 23:57:16 +00:00
|
|
|
async function run() {
|
|
|
|
let result: ResolvedHost;
|
2023-03-22 00:43:55 +00:00
|
|
|
|
2023-05-30 23:57:16 +00:00
|
|
|
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
|
|
|
|
2023-05-30 23:57:16 +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)
|
2023-05-30 23:57:16 +00:00
|
|
|
);
|
|
|
|
if (random === undefined) {
|
|
|
|
callback(
|
|
|
|
new Error(`Hostname: ${hostname} cannot be resolved`),
|
|
|
|
'',
|
|
|
|
-1
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
callback(null, random.address, random.family);
|
|
|
|
return;
|
|
|
|
}
|
2023-03-22 00:43:55 +00:00
|
|
|
|
2023-06-05 19:55:09 +00:00
|
|
|
callback(null, addresses);
|
2023-05-30 23:57:16 +00:00
|
|
|
} catch (error) {
|
|
|
|
callback(error, []);
|
2023-03-22 00:43:55 +00:00
|
|
|
}
|
2023-05-30 23:57:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
drop(run());
|
2023-03-22 00:43:55 +00:00
|
|
|
}
|
2023-05-30 23:57:16 +00:00
|
|
|
|
|
|
|
// Note: `nodeLookup` has a complicated type due to compatibility requirements.
|
|
|
|
export const electronLookup = lookupAll as typeof nodeLookup;
|