mirror of
https://github.com/SukkaW/Surge.git
synced 2026-09-12 18:44:36 +08:00
Chore: sort Telegram DC Options
This commit is contained in:
@@ -3,6 +3,7 @@ import { expect } from 'earl';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
DC_OPTION_FLAG_IPV6,
|
DC_OPTION_FLAG_IPV6,
|
||||||
|
DC_OPTION_FLAG_MEDIA_ONLY,
|
||||||
DC_OPTION_FLAG_STATIC,
|
DC_OPTION_FLAG_STATIC,
|
||||||
mergeFallbackEndpoints,
|
mergeFallbackEndpoints,
|
||||||
TELEGRAM_BOOTSTRAP_ENDPOINTS
|
TELEGRAM_BOOTSTRAP_ENDPOINTS
|
||||||
@@ -69,6 +70,53 @@ describe('MTProto DC config', () => {
|
|||||||
expect(result.bootstrapAdded).toEqual(TELEGRAM_BOOTSTRAP_ENDPOINTS.length - 1);
|
expect(result.bootstrapAdded).toEqual(TELEGRAM_BOOTSTRAP_ENDPOINTS.length - 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('emits a deterministic option order regardless of input order', () => {
|
||||||
|
const options = [
|
||||||
|
{ id: 2, ip: '149.154.167.222', port: 443, flags: DC_OPTION_FLAG_MEDIA_ONLY },
|
||||||
|
{ id: 1, ip: '2001:0b28:f23d:f001:0000:0000:0000:000a', port: 443, flags: DC_OPTION_FLAG_IPV6 },
|
||||||
|
// Numerically before .222 but lexically after it.
|
||||||
|
{ id: 2, ip: '149.154.167.41', port: 443, flags: DC_OPTION_FLAG_STATIC },
|
||||||
|
{ id: 1, ip: '149.154.175.56', port: 443, flags: 0 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const build = (input: typeof options) => {
|
||||||
|
const config: MTProtoDCConfig = {
|
||||||
|
version: 1,
|
||||||
|
date: 1,
|
||||||
|
expires: 2,
|
||||||
|
this_dc: 1,
|
||||||
|
options: structuredClone(input)
|
||||||
|
};
|
||||||
|
mergeFallbackEndpoints(config, []);
|
||||||
|
return config.options;
|
||||||
|
};
|
||||||
|
|
||||||
|
const forward = build(options);
|
||||||
|
expect(build([...options].reverse())).toEqual(forward);
|
||||||
|
|
||||||
|
const ipsOfDc = (id: number) => forward.reduce<string[]>((acc, option) => {
|
||||||
|
if (option.id === id) acc.push(option.ip);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// IPv4 sorts numerically and precedes IPv6 within the same DC.
|
||||||
|
expect(ipsOfDc(1)).toEqual([
|
||||||
|
'149.154.175.50',
|
||||||
|
'149.154.175.56',
|
||||||
|
'2001:b28:f23d:f001::a'
|
||||||
|
]);
|
||||||
|
expect(ipsOfDc(2)).toEqual([
|
||||||
|
'95.161.76.100',
|
||||||
|
'149.154.167.41',
|
||||||
|
'149.154.167.50',
|
||||||
|
'149.154.167.51',
|
||||||
|
'149.154.167.222',
|
||||||
|
'2001:67c:4e8:f002::a'
|
||||||
|
]);
|
||||||
|
// DC ids are non-decreasing across the whole array.
|
||||||
|
expect(forward.every((option, i) => i === 0 || forward[i - 1].id <= option.id)).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps functional variants separate and preserves backup secrets', () => {
|
it('keeps functional variants separate and preserves backup secrets', () => {
|
||||||
const config: MTProtoDCConfig = {
|
const config: MTProtoDCConfig = {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type { TelegramBackupEndpoint } from './get-telegram-backup-ip';
|
|||||||
import { setBit, getBit } from 'foxts/bitwise';
|
import { setBit, getBit } from 'foxts/bitwise';
|
||||||
import { bigint2ip, ip2bigint } from 'fast-cidr-tools';
|
import { bigint2ip, ip2bigint } from 'fast-cidr-tools';
|
||||||
import { isProbablyIpv6 } from 'foxts/is-probably-ip';
|
import { isProbablyIpv6 } from 'foxts/is-probably-ip';
|
||||||
|
import { fastIpVersion } from 'foxts/fast-ip-version';
|
||||||
|
|
||||||
export const DC_OPTION_FLAG_IPV6 = 1 << 0;
|
export const DC_OPTION_FLAG_IPV6 = 1 << 0;
|
||||||
export const DC_OPTION_FLAG_MEDIA_ONLY = 1 << 1;
|
export const DC_OPTION_FLAG_MEDIA_ONLY = 1 << 1;
|
||||||
@@ -148,6 +149,39 @@ function mergeEndpoint(config: MTProtoDCConfig, endpoint: MTProtoEndpoint) {
|
|||||||
return !matched;
|
return !matched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The output order is otherwise a mix of whatever help.getConfig returned and
|
||||||
|
* the order the concurrent backup-endpoint lookups happened to resolve in, so
|
||||||
|
* two builds over identical upstream data can produce a spurious diff. Sort on
|
||||||
|
* the full option identity to keep the committed JSON reviewable.
|
||||||
|
*/
|
||||||
|
function sortOptions(config: MTProtoDCConfig) {
|
||||||
|
config.options.sort((a, b) => {
|
||||||
|
if (a.id !== b.id) return a.id - b.id;
|
||||||
|
|
||||||
|
// IPv4 before IPv6, each ordered numerically rather than lexically so
|
||||||
|
// 149.154.167.51 sorts before 149.154.167.222.
|
||||||
|
const aVersion = fastIpVersion(a.ip);
|
||||||
|
const bVersion = fastIpVersion(b.ip);
|
||||||
|
if (aVersion !== bVersion) return aVersion - bVersion;
|
||||||
|
|
||||||
|
if (aVersion === 0 || bVersion === 0) return 0; // how is invalid ip even here? just leave them in their original order
|
||||||
|
|
||||||
|
const aIp = ip2bigint(a.ip, aVersion);
|
||||||
|
const bIp = ip2bigint(b.ip, bVersion);
|
||||||
|
if (aIp !== bIp) return aIp < bIp ? -1 : 1;
|
||||||
|
|
||||||
|
if (a.port !== b.port) return a.port - b.port;
|
||||||
|
if (a.flags !== b.flags) return a.flags - b.flags;
|
||||||
|
|
||||||
|
// Secrets are base64, so a plain code-unit compare is stable and locale-free.
|
||||||
|
const aSecret = a.secret ?? '';
|
||||||
|
const bSecret = b.secret ?? '';
|
||||||
|
if (aSecret === bSecret) return 0;
|
||||||
|
return aSecret < bSecret ? -1 : 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function deduplicateOptions(config: MTProtoDCConfig) {
|
function deduplicateOptions(config: MTProtoDCConfig) {
|
||||||
const previousCount = config.options.length;
|
const previousCount = config.options.length;
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
@@ -186,9 +220,12 @@ export function mergeFallbackEndpoints(
|
|||||||
if (mergeEndpoint(config, endpoint)) bootstrapAdded++;
|
if (mergeEndpoint(config, endpoint)) bootstrapAdded++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const duplicatesRemoved = deduplicateOptions(config);
|
||||||
|
sortOptions(config);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
backupAdded,
|
backupAdded,
|
||||||
bootstrapAdded,
|
bootstrapAdded,
|
||||||
duplicatesRemoved: deduplicateOptions(config)
|
duplicatesRemoved
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user