import { connect } from 'cloudflare:sockets';
let at = '351c9981-04b6-4103-aa4b-864aa9c91469';
let fallbackAddress = '';
let fallbackPort = '443';
let socks5Config = '';
let customPreferredIPs = [];
let customPreferredDomains = [];
let enableSocksDowngrade = false;
let disableNonTLS = false;
let disablePreferred = false;
let enableRegionMatching = true;
let currentWorkerRegion = '';
let manualWorkerRegion = '';
let piu = '';
let cp = '';
let ev = true;
let et = false;
let ex = false;
let tp = '';
let scu = 'https://url.v1.mk/sub';
let epd = true;
let epi = true;
let egi = true;
let kvStore = null;
let kvConfig = {};
const regionMapping = {
'US': ['🇺🇸 美国', 'US', 'United States'],
'SG': ['🇸🇬 新加坡', 'SG', 'Singapore'],
'JP': ['🇯🇵 日本', 'JP', 'Japan'],
'HK': ['🇭🇰 香港', 'HK', 'Hong Kong'],
'KR': ['🇰🇷 韩国', 'KR', 'South Korea'],
'DE': ['🇩🇪 德国', 'DE', 'Germany'],
'SE': ['🇸🇪 瑞典', 'SE', 'Sweden'],
'NL': ['🇳🇱 荷兰', 'NL', 'Netherlands'],
'FI': ['🇫🇮 芬兰', 'FI', 'Finland'],
'GB': ['🇬🇧 英国', 'GB', 'United Kingdom'],
'Oracle': ['甲骨文', 'Oracle'],
'DigitalOcean': ['数码海', 'DigitalOcean'],
'Vultr': ['Vultr', 'Vultr'],
'Multacom': ['Multacom', 'Multacom']
};
let backupIPs = [
{ domain: 'ProxyIP.US.CMLiussss.net', region: 'US', regionCode: 'US', port: 443 },
{ domain: 'ProxyIP.SG.CMLiussss.net', region: 'SG', regionCode: 'SG', port: 443 },
{ domain: 'ProxyIP.JP.CMLiussss.net', region: 'JP', regionCode: 'JP', port: 443 },
{ domain: 'ProxyIP.HK.CMLiussss.net', region: 'HK', regionCode: 'HK', port: 443 },
{ domain: 'ProxyIP.KR.CMLiussss.net', region: 'KR', regionCode: 'KR', port: 443 },
{ domain: 'ProxyIP.DE.CMLiussss.net', region: 'DE', regionCode: 'DE', port: 443 },
{ domain: 'ProxyIP.SE.CMLiussss.net', region: 'SE', regionCode: 'SE', port: 443 },
{ domain: 'ProxyIP.NL.CMLiussss.net', region: 'NL', regionCode: 'NL', port: 443 },
{ domain: 'ProxyIP.FI.CMLiussss.net', region: 'FI', regionCode: 'FI', port: 443 },
{ domain: 'ProxyIP.GB.CMLiussss.net', region: 'GB', regionCode: 'GB', port: 443 },
{ domain: 'ProxyIP.Oracle.cmliussss.net', region: 'Oracle', regionCode: 'Oracle', port: 443 },
{ domain: 'ProxyIP.DigitalOcean.CMLiussss.net', region: 'DigitalOcean', regionCode: 'DigitalOcean', port: 443 },
{ domain: 'ProxyIP.Vultr.CMLiussss.net', region: 'Vultr', regionCode: 'Vultr', port: 443 },
{ domain: 'ProxyIP.Multacom.CMLiussss.net', region: 'Multacom', regionCode: 'Multacom', port: 443 }
];
const directDomains = [
{ name: "cloudflare.182682.xyz", domain: "cloudflare.182682.xyz" }, { name: "speed.marisalnc.com", domain: "speed.marisalnc.com" },
{ domain: "freeyx.cloudflare88.eu.org" }, { domain: "bestcf.top" }, { domain: "cdn.2020111.xyz" }, { domain: "cfip.cfcdn.vip" },
{ domain: "cf.0sm.com" }, { domain: "cf.090227.xyz" }, { domain: "cf.zhetengsha.eu.org" }, { domain: "cloudflare.9jy.cc" },
{ domain: "cf.zerone-cdn.pp.ua" }, { domain: "cfip.1323123.xyz" }, { domain: "cnamefuckxxs.yuchen.icu" }, { domain: "cloudflare-ip.mofashi.ltd" },
{ domain: "115155.xyz" }, { domain: "cname.xirancdn.us" }, { domain: "f3058171cad.002404.xyz" }, { domain: "8.889288.xyz" },
{ domain: "cdn.tzpro.xyz" }, { domain: "cf.877771.xyz" }, { domain: "xn--b6gac.eu.org" }
];
const E_INVALID_DATA = atob('aW52YWxpZCBkYXRh');
const E_INVALID_USER = atob('aW52YWxpZCB1c2Vy');
const E_UNSUPPORTED_CMD = atob('Y29tbWFuZCBpcyBub3Qgc3VwcG9ydGVk');
const E_UDP_DNS_ONLY = atob('VURQIHByb3h5IG9ubHkgZW5hYmxlIGZvciBETlMgd2hpY2ggaXMgcG9ydCA1Mw==');
const E_INVALID_ADDR_TYPE = atob('aW52YWxpZCBhZGRyZXNzVHlwZQ==');
const E_EMPTY_ADDR = atob('YWRkcmVzc1ZhbHVlIGlzIGVtcHR5');
const E_WS_NOT_OPEN = atob('d2ViU29ja2V0LmVhZHlTdGF0ZSBpcyBub3Qgb3Blbg==');
const E_INVALID_ID_STR = atob('U3RyaW5naWZpZWQgaWRlbnRpZmllciBpcyBpbnZhbGlk');
const E_INVALID_SOCKS_ADDR = atob('SW52YWxpZCBTT0NLUyBhZGRyZXNzIGZvcm1hdA==');
const E_SOCKS_NO_METHOD = atob('bm8gYWNjZXB0YWJsZSBtZXRob2Rz');
const E_SOCKS_AUTH_NEEDED = atob('c29ja3Mgc2VydmVyIG5lZWRzIGF1dGg=');
const E_SOCKS_AUTH_FAIL = atob('ZmFpbCB0byBhdXRoIHNvY2tzIHNlcnZlcg==');
const E_SOCKS_CONN_FAIL = atob('ZmFpbCB0byBvcGVuIHNvY2tzIGNvbm5lY3Rpb24=');
let parsedSocks5Config = {};
let isSocksEnabled = false;
const ADDRESS_TYPE_IPV4 = 1;
const ADDRESS_TYPE_URL = 2;
const ADDRESS_TYPE_IPV6 = 3;
function isValidFormat(str) {
const userRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
return userRegex.test(str);
}
function isValidIP(ip) {
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
if (ipv4Regex.test(ip)) return true;
const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
if (ipv6Regex.test(ip)) return true;
const ipv6ShortRegex = /^::1$|^::$|^(?:[0-9a-fA-F]{1,4}:)*::(?:[0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4}$/;
if (ipv6ShortRegex.test(ip)) return true;
return false;
}
async function initKVStore(env) {
if (env.C) {
try {
kvStore = env.C;
await loadKVConfig();
} catch (error) {
kvStore = null;
}
} else {
}
}
async function loadKVConfig() {
if (!kvStore) {
return;
}
try {
const configData = await kvStore.get('c');
if (configData) {
kvConfig = JSON.parse(configData);
} else {
}
} catch (error) {
kvConfig = {};
}
}
async function saveKVConfig() {
if (!kvStore) {
return;
}
try {
const configString = JSON.stringify(kvConfig);
await kvStore.put('c', configString);
} catch (error) {
throw error;
}
}
function getConfigValue(key, defaultValue = '') {
if (kvConfig[key] !== undefined) {
return kvConfig[key];
}
return defaultValue;
}
async function setConfigValue(key, value) {
kvConfig[key] = value;
await saveKVConfig();
}
async function detectWorkerRegion(request) {
try {
const cfCountry = request.cf?.country;
if (cfCountry) {
const countryToRegion = {
'US': 'US', 'SG': 'SG', 'JP': 'JP', 'HK': 'HK', 'KR': 'KR',
'DE': 'DE', 'SE': 'SE', 'NL': 'NL', 'FI': 'FI', 'GB': 'GB',
'CN': 'HK', 'TW': 'HK', 'AU': 'SG', 'CA': 'US',
'FR': 'DE', 'IT': 'DE', 'ES': 'DE', 'CH': 'DE',
'AT': 'DE', 'BE': 'NL', 'DK': 'SE', 'NO': 'SE', 'IE': 'GB'
};
if (countryToRegion[cfCountry]) {
return countryToRegion[cfCountry];
}
}
return 'HK';
} catch (error) {
return 'HK';
}
}
async function checkIPAvailability(domain, port = 443, timeout = 2000) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(`https://${domain}`, {
method: 'HEAD',
signal: controller.signal,
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; CF-IP-Checker/1.0)'
}
});
clearTimeout(timeoutId);
return response.status < 500;
} catch (error) {
return true;
}
}
async function getBestBackupIP(workerRegion = '') {
if (backupIPs.length === 0) {
return null;
}
const availableIPs = backupIPs.map(ip => ({ ...ip, available: true }));
if (enableRegionMatching && workerRegion) {
const sortedIPs = getSmartRegionSelection(workerRegion, availableIPs);
if (sortedIPs.length > 0) {
const selectedIP = sortedIPs[0];
return selectedIP;
}
}
const selectedIP = availableIPs[0];
return selectedIP;
}
function getNearbyRegions(region) {
const nearbyMap = {
'US': ['SG', 'JP', 'HK', 'KR'],
'SG': ['JP', 'HK', 'KR', 'US'],
'JP': ['SG', 'HK', 'KR', 'US'],
'HK': ['SG', 'JP', 'KR', 'US'],
'KR': ['JP', 'HK', 'SG', 'US'],
'DE': ['NL', 'GB', 'SE', 'FI'],
'SE': ['DE', 'NL', 'FI', 'GB'],
'NL': ['DE', 'GB', 'SE', 'FI'],
'FI': ['SE', 'DE', 'NL', 'GB'],
'GB': ['DE', 'NL', 'SE', 'FI']
};
return nearbyMap[region] || [];
}
function getAllRegionsByPriority(region) {
const nearbyRegions = getNearbyRegions(region);
const allRegions = ['US', 'SG', 'JP', 'HK', 'KR', 'DE', 'SE', 'NL', 'FI', 'GB'];
return [region, ...nearbyRegions, ...allRegions.filter(r => r !== region && !nearbyRegions.includes(r))];
}
function getSmartRegionSelection(workerRegion, availableIPs) {
if (!enableRegionMatching || !workerRegion) {
return availableIPs;
}
const priorityRegions = getAllRegionsByPriority(workerRegion);
const sortedIPs = [];
for (const region of priorityRegions) {
const regionIPs = availableIPs.filter(ip => ip.regionCode === region);
sortedIPs.push(...regionIPs);
}
return sortedIPs;
}
function parseAddressAndPort(input) {
if (input.includes('[') && input.includes(']')) {
const match = input.match(/^\[([^\]]+)\](?::(\d+))?$/);
if (match) {
return {
address: match[1],
port: match[2] ? parseInt(match[2], 10) : null
};
}
}
const lastColonIndex = input.lastIndexOf(':');
if (lastColonIndex > 0) {
const address = input.substring(0, lastColonIndex);
const portStr = input.substring(lastColonIndex + 1);
const port = parseInt(portStr, 10);
if (!isNaN(port) && port > 0 && port <= 65535) {
return { address, port };
}
}
return { address: input, port: null };
}
export default {
async fetch(request, env, ctx) {
try {
await initKVStore(env);
at = (env.u || env.U || at).toLowerCase();
const subPath = (env.d || env.D || at).toLowerCase();
const ci = getConfigValue('p', env.p || env.P);
let useCustomIP = false;
const manualRegion = getConfigValue('wk', env.wk || env.WK);
if (manualRegion && manualRegion.trim()) {
manualWorkerRegion = manualRegion.trim().toUpperCase();
currentWorkerRegion = manualWorkerRegion;
} else if (ci && ci.trim()) {
useCustomIP = true;
currentWorkerRegion = 'CUSTOM';
} else {
currentWorkerRegion = await detectWorkerRegion(request);
}
const regionMatchingControl = env.rm || env.RM;
if (regionMatchingControl && regionMatchingControl.toLowerCase() === 'no') {
enableRegionMatching = false;
}
const envFallback = getConfigValue('p', env.p || env.P);
if (envFallback) {
const fallbackValue = envFallback.toLowerCase();
if (fallbackValue.includes(']:')) {
const lastColonIndex = fallbackValue.lastIndexOf(':');
fallbackPort = fallbackValue.slice(lastColonIndex + 1);
fallbackAddress = fallbackValue.slice(0, lastColonIndex);
} else if (!fallbackValue.includes(']:') && !fallbackValue.includes(']')) {
[fallbackAddress, fallbackPort = '443'] = fallbackValue.split(':');
} else {
fallbackAddress = fallbackValue;
fallbackPort = '443';
}
}
socks5Config = getConfigValue('s', env.s || env.S) || socks5Config;
if (socks5Config) {
try {
parsedSocks5Config = parseSocksConfig(socks5Config);
isSocksEnabled = true;
} catch (err) {
isSocksEnabled = false;
}
}
const customPreferred = getConfigValue('yx', env.yx || env.YX);
if (customPreferred) {
try {
const preferredList = customPreferred.split(',').map(item => item.trim()).filter(item => item);
customPreferredIPs = [];
customPreferredDomains = [];
preferredList.forEach(item => {
let nodeName = '';
let addressPart = item;
if (item.includes('#')) {
const parts = item.split('#');
addressPart = parts[0].trim();
nodeName = parts[1].trim();
}
const { address, port } = parseAddressAndPort(addressPart);
if (!nodeName) {
nodeName = '自定义优选-' + address + (port ? ':' + port : '');
}
if (isValidIP(address)) {
customPreferredIPs.push({
ip: address,
port: port,
isp: nodeName
});
} else {
customPreferredDomains.push({
domain: address,
port: port,
name: nodeName
});
}
});
} catch (err) {
customPreferredIPs = [];
customPreferredDomains = [];
}
}
const dc = env.qj || env.QJ;
if (dc && downgradeControl.toLowerCase() === 'no') {
enableSocksDowngrade = true;
}
const dkbyControl = getConfigValue('dkby', env.dkby || env.DKBY);
if (dkbyControl && dkbyControl.toLowerCase() === 'yes') {
disableNonTLS = true;
}
const yxbyControl = env.yxby || env.YXBY;
if (yxbyControl && yxbyControl.toLowerCase() === 'yes') {
disablePreferred = true;
}
const vlessControl = getConfigValue('ev', env.ev);
if (vlessControl !== undefined && vlessControl !== '') {
ev = vlessControl === 'yes' || vlessControl === true || vlessControl === 'true';
}
const tjControl = getConfigValue('et', env.et);
if (tjControl !== undefined && tjControl !== '') {
et = tjControl === 'yes' || tjControl === true || tjControl === 'true';
}
tp = getConfigValue('tp', env.tp) || '';
const xhttpControl = getConfigValue('ex', env.ex);
if (xhttpControl !== undefined && xhttpControl !== '') {
ex = xhttpControl === 'yes' || xhttpControl === true || xhttpControl === 'true';
}
scu = getConfigValue('scu', env.scu) || 'https://url.v1.mk/sub';
const preferredDomainsControl = getConfigValue('epd', env.epd);
if (preferredDomainsControl !== undefined && preferredDomainsControl !== '') {
epd = preferredDomainsControl !== 'no' && preferredDomainsControl !== false && preferredDomainsControl !== 'false';
}
const preferredIPsControl = getConfigValue('epi', env.epi);
if (preferredIPsControl !== undefined && preferredIPsControl !== '') {
epi = preferredIPsControl !== 'no' && preferredIPsControl !== false && preferredIPsControl !== 'false';
}
const githubIPsControl = getConfigValue('egi', env.egi);
if (githubIPsControl !== undefined && githubIPsControl !== '') {
egi = githubIPsControl !== 'no' && githubIPsControl !== false && githubIPsControl !== 'false';
}
if (!ev && !et && !ex) {
ev = true;
}
piu = getConfigValue('yxURL', env.yxURL || env.YXURL) || 'https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt';
cp = getConfigValue('d', env.d || env.D) || '';
const defaultURL = 'https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt';
if (piu !== defaultURL) {
directDomains.length = 0;
customPreferredIPs = [];
customPreferredDomains = [];
}
const url = new URL(request.url);
if (url.pathname.includes('/api/config')) {
const pathParts = url.pathname.split('/').filter(p => p);
const apiIndex = pathParts.indexOf('api');
if (apiIndex > 0) {
const pathIdentifier = pathParts[apiIndex - 1];
let isValid = false;
if (cp && cp.trim()) {
const cleanCustomPath = (cp.startsWith('/') ? cp : '/' + cp).replace(/^\//, '');
isValid = (pathIdentifier === cleanCustomPath);
} else {
isValid = (isValidFormat(pathIdentifier) && pathIdentifier === at);
}
if (isValid) {
return await handleConfigAPI(request);
} else {
return new Response(JSON.stringify({ error: '路径验证失败' }), {
status: 403,
headers: { 'Content-Type': 'application/json' }
});
}
}
return new Response(JSON.stringify({ error: '无效的API路径' }), {
status: 404,
headers: { 'Content-Type': 'application/json' }
});
}
if (url.pathname.includes('/api/preferred-ips')) {
const pathParts = url.pathname.split('/').filter(p => p);
const apiIndex = pathParts.indexOf('api');
if (apiIndex > 0) {
const pathIdentifier = pathParts[apiIndex - 1];
let isValid = false;
if (cp && cp.trim()) {
const cleanCustomPath = (cp.startsWith('/') ? cp : '/' + cp).replace(/^\//, '');
isValid = (pathIdentifier === cleanCustomPath);
} else {
isValid = (isValidFormat(pathIdentifier) && pathIdentifier === at);
}
if (isValid) {
return await handlePreferredIPsAPI(request);
} else {
return new Response(JSON.stringify({ error: '路径验证失败' }), {
status: 403,
headers: { 'Content-Type': 'application/json' }
});
}
}
return new Response(JSON.stringify({ error: '无效的API路径' }), {
status: 404,
headers: { 'Content-Type': 'application/json' }
});
}
if (request.method === 'POST' && ex) {
const r = await handleXhttpPost(request);
if (r) {
ctx.waitUntil(r.closed);
return new Response(r.readable, {
headers: {
'X-Accel-Buffering': 'no',
'Cache-Control': 'no-store',
Connection: 'keep-alive',
'User-Agent': 'Go-http-client/2.0',
'Content-Type': 'application/grpc',
},
});
}
return new Response('Internal Server Error', { status: 500 });
}
if (request.headers.get('Upgrade') === atob('d2Vic29ja2V0')) {
return await handleWsRequest(request);
}
if (request.method === 'GET') {
if (url.pathname === '/region') {
const ci = getConfigValue('p', env.p || env.P);
const manualRegion = getConfigValue('wk', env.wk || env.WK);
if (manualRegion && manualRegion.trim()) {
return new Response(JSON.stringify({
region: manualRegion.trim().toUpperCase(),
detectionMethod: '手动指定地区',
manualRegion: manualRegion.trim().toUpperCase(),
timestamp: new Date().toISOString()
}), {
headers: { 'Content-Type': 'application/json' }
});
} else if (ci && ci.trim()) {
return new Response(JSON.stringify({
region: 'CUSTOM',
detectionMethod: '自定义ProxyIP模式', ci: ci,
timestamp: new Date().toISOString()
}), {
headers: { 'Content-Type': 'application/json' }
});
} else {
const detectedRegion = await detectWorkerRegion(request);
return new Response(JSON.stringify({
region: detectedRegion,
detectionMethod: 'API检测',
timestamp: new Date().toISOString()
}), {
headers: { 'Content-Type': 'application/json' }
});
}
}
if (url.pathname === '/test-api') {
try {
const testRegion = await detectWorkerRegion(request);
return new Response(JSON.stringify({
detectedRegion: testRegion,
message: 'API测试完成',
timestamp: new Date().toISOString()
}), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({
error: error.message,
message: 'API测试失败'
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
if (url.pathname === '/') {
const terminalHtml = `
/g;
const cellRegex = /| (.+?)<\/td>[\s\S]*? | ([\d.:a-fA-F]+)<\/td>/;
let match;
while ((match = rowRegex.exec(html)) !== null) {
const rowHtml = match[0];
const cellMatch = rowHtml.match(cellRegex);
if (cellMatch && cellMatch[1] && cellMatch[2]) {
results.push({
isp: cellMatch[1].trim().replace(/<.*?>/g, ''),
ip: cellMatch[2].trim()
});
}
}
if (results.length === 0) {
}
return results;
} catch (error) {
return [];
}
}
async function handleWsRequest(request) {
const wsPair = new WebSocketPair();
const [clientSock, serverSock] = Object.values(wsPair);
serverSock.accept();
let remoteConnWrapper = { socket: null };
let isDnsQuery = false;
let protocolType = null;
const earlyData = request.headers.get(atob('c2VjLXdlYnNvY2tldC1wcm90b2NvbA==')) || '';
const readable = makeReadableStream(serverSock, earlyData);
readable.pipeTo(new WritableStream({
async write(chunk) {
if (isDnsQuery) return await forwardUDP(chunk, serverSock, null);
if (remoteConnWrapper.socket) {
const writer = remoteConnWrapper.socket.writable.getWriter();
await writer.write(chunk);
writer.releaseLock();
return;
}
if (!protocolType) {
if (ev && chunk.byteLength >= 24) {
const vlessResult = parseWsPacketHeader(chunk, at);
if (!vlessResult.hasError) {
protocolType = 'vless';
const { addressType, port, hostname, rawIndex, version, isUDP } = vlessResult;
if (isUDP) {
if (port === 53) isDnsQuery = true;
else throw new Error(E_UDP_DNS_ONLY);
}
const respHeader = new Uint8Array([version[0], 0]);
const rawData = chunk.slice(rawIndex);
if (isDnsQuery) return forwardUDP(rawData, serverSock, respHeader);
await forwardTCP(addressType, hostname, port, rawData, serverSock, respHeader, remoteConnWrapper);
return;
}
}
if (et && chunk.byteLength >= 56) {
const tjResult = await parseTrojanHeader(chunk, at);
if (!tjResult.hasError) {
protocolType = atob('dHJvamFu');
const { addressType, port, hostname, rawClientData } = tjResult;
await forwardTCP(addressType, hostname, port, rawClientData, serverSock, null, remoteConnWrapper);
return;
}
}
throw new Error('Invalid protocol or authentication failed');
}
},
})).catch((err) => { });
return new Response(null, { status: 101, webSocket: clientSock });
}
async function forwardTCP(addrType, host, portNum, rawData, ws, respHeader, remoteConnWrapper) {
async function connectAndSend(address, port, useSocks = false) {
const remoteSock = useSocks ?
await establishSocksConnection(addrType, address, port) :
connect({ hostname: address, port: port });
const writer = remoteSock.writable.getWriter();
await writer.write(rawData);
writer.releaseLock();
return remoteSock;
}
async function retryConnection() {
if (enableSocksDowngrade && isSocksEnabled) {
try {
const socksSocket = await connectAndSend(host, portNum, true);
remoteConnWrapper.socket = socksSocket;
socksSocket.closed.catch(() => {}).finally(() => closeSocketQuietly(ws));
connectStreams(socksSocket, ws, respHeader, null);
return;
} catch (socksErr) {
let backupHost, backupPort;
if (fallbackAddress && fallbackPort) {
backupHost = fallbackAddress;
backupPort = parseInt(fallbackPort, 10) || portNum;
} else {
const bestBackupIP = await getBestBackupIP(currentWorkerRegion);
backupHost = bestBackupIP ? bestBackupIP.domain : host;
backupPort = bestBackupIP ? bestBackupIP.port : portNum;
}
try {
const fallbackSocket = await connectAndSend(backupHost, backupPort, false);
remoteConnWrapper.socket = fallbackSocket;
fallbackSocket.closed.catch(() => {}).finally(() => closeSocketQuietly(ws));
connectStreams(fallbackSocket, ws, respHeader, null);
} catch (fallbackErr) {
closeSocketQuietly(ws);
}
}
} else {
let backupHost, backupPort;
if (fallbackAddress && fallbackPort) {
backupHost = fallbackAddress;
backupPort = parseInt(fallbackPort, 10) || portNum;
} else {
const bestBackupIP = await getBestBackupIP(currentWorkerRegion);
backupHost = bestBackupIP ? bestBackupIP.domain : host;
backupPort = bestBackupIP ? bestBackupIP.port : portNum;
}
try {
const fallbackSocket = await connectAndSend(backupHost, backupPort, isSocksEnabled);
remoteConnWrapper.socket = fallbackSocket;
fallbackSocket.closed.catch(() => {}).finally(() => closeSocketQuietly(ws));
connectStreams(fallbackSocket, ws, respHeader, null);
} catch (fallbackErr) {
closeSocketQuietly(ws);
}
}
}
try {
const initialSocket = await connectAndSend(host, portNum, enableSocksDowngrade ? false : isSocksEnabled);
remoteConnWrapper.socket = initialSocket;
connectStreams(initialSocket, ws, respHeader, retryConnection);
} catch (err) {
retryConnection();
}
}
function parseWsPacketHeader(chunk, token) {
if (chunk.byteLength < 24) return { hasError: true, message: E_INVALID_DATA };
const version = new Uint8Array(chunk.slice(0, 1));
if (formatIdentifier(new Uint8Array(chunk.slice(1, 17))) !== token) return { hasError: true, message: E_INVALID_USER };
const optLen = new Uint8Array(chunk.slice(17, 18))[0];
const cmd = new Uint8Array(chunk.slice(18 + optLen, 19 + optLen))[0];
let isUDP = false;
if (cmd === 1) {} else if (cmd === 2) { isUDP = true; } else { return { hasError: true, message: E_UNSUPPORTED_CMD }; }
const portIdx = 19 + optLen;
const port = new DataView(chunk.slice(portIdx, portIdx + 2)).getUint16(0);
let addrIdx = portIdx + 2, addrLen = 0, addrValIdx = addrIdx + 1, hostname = '';
const addressType = new Uint8Array(chunk.slice(addrIdx, addrValIdx))[0];
switch (addressType) {
case ADDRESS_TYPE_IPV4: addrLen = 4; hostname = new Uint8Array(chunk.slice(addrValIdx, addrValIdx + addrLen)).join('.'); break;
case ADDRESS_TYPE_URL: addrLen = new Uint8Array(chunk.slice(addrValIdx, addrValIdx + 1))[0]; addrValIdx += 1; hostname = new TextDecoder().decode(chunk.slice(addrValIdx, addrValIdx + addrLen)); break;
case ADDRESS_TYPE_IPV6: addrLen = 16; const ipv6 = []; const ipv6View = new DataView(chunk.slice(addrValIdx, addrValIdx + addrLen)); for (let i = 0; i < 8; i++) ipv6.push(ipv6View.getUint16(i * 2).toString(16)); hostname = ipv6.join(':'); break;
default: return { hasError: true, message: `${E_INVALID_ADDR_TYPE}: ${addressType}` };
}
if (!hostname) return { hasError: true, message: `${E_EMPTY_ADDR}: ${addressType}` };
return { hasError: false, addressType, port, hostname, isUDP, rawIndex: addrValIdx + addrLen, version };
}
function makeReadableStream(socket, earlyDataHeader) {
let cancelled = false;
return new ReadableStream({
start(controller) {
socket.addEventListener('message', (event) => { if (!cancelled) controller.enqueue(event.data); });
socket.addEventListener('close', () => { if (!cancelled) { closeSocketQuietly(socket); controller.close(); } });
socket.addEventListener('error', (err) => controller.error(err));
const { earlyData, error } = base64ToArray(earlyDataHeader);
if (error) controller.error(error); else if (earlyData) controller.enqueue(earlyData);
},
cancel() { cancelled = true; closeSocketQuietly(socket); }
});
}
async function connectStreams(remoteSocket, webSocket, headerData, retryFunc) {
let header = headerData, hasData = false;
await remoteSocket.readable.pipeTo(
new WritableStream({
async write(chunk, controller) {
hasData = true;
if (webSocket.readyState !== 1) controller.error(E_WS_NOT_OPEN);
if (header) { webSocket.send(await new Blob([header, chunk]).arrayBuffer()); header = null; }
else { webSocket.send(chunk); }
},
abort(reason) { },
})
).catch((error) => { closeSocketQuietly(webSocket); });
if (!hasData && retryFunc) retryFunc();
}
async function forwardUDP(udpChunk, webSocket, respHeader) {
try {
const tcpSocket = connect({ hostname: '8.8.4.4', port: 53 });
let header = respHeader;
const writer = tcpSocket.writable.getWriter();
await writer.write(udpChunk);
writer.releaseLock();
await tcpSocket.readable.pipeTo(new WritableStream({
async write(chunk) {
if (webSocket.readyState === 1) {
if (header) { webSocket.send(await new Blob([header, chunk]).arrayBuffer()); header = null; }
else { webSocket.send(chunk); }
}
},
}));
} catch (error) { }
}
async function establishSocksConnection(addrType, address, port) {
const { username, password, hostname, socksPort } = parsedSocks5Config;
const socket = connect({ hostname, port: socksPort });
const writer = socket.writable.getWriter();
await writer.write(new Uint8Array(username ? [5, 2, 0, 2] : [5, 1, 0]));
const reader = socket.readable.getReader();
let res = (await reader.read()).value;
if (res[0] !== 5 || res[1] === 255) throw new Error(E_SOCKS_NO_METHOD);
if (res[1] === 2) {
if (!username || !password) throw new Error(E_SOCKS_AUTH_NEEDED);
const encoder = new TextEncoder();
const authRequest = new Uint8Array([1, username.length, ...encoder.encode(username), password.length, ...encoder.encode(password)]);
await writer.write(authRequest);
res = (await reader.read()).value;
if (res[0] !== 1 || res[1] !== 0) throw new Error(E_SOCKS_AUTH_FAIL);
}
const encoder = new TextEncoder(); let DSTADDR;
switch (addrType) {
case ADDRESS_TYPE_IPV4: DSTADDR = new Uint8Array([1, ...address.split('.').map(Number)]); break;
case ADDRESS_TYPE_URL: DSTADDR = new Uint8Array([3, address.length, ...encoder.encode(address)]); break;
case ADDRESS_TYPE_IPV6: DSTADDR = new Uint8Array([4, ...address.split(':').flatMap(x => [parseInt(x.slice(0, 2), 16), parseInt(x.slice(2), 16)])]); break;
default: throw new Error(E_INVALID_ADDR_TYPE);
}
await writer.write(new Uint8Array([5, 1, 0, ...DSTADDR, port >> 8, port & 255]));
res = (await reader.read()).value;
if (res[1] !== 0) throw new Error(E_SOCKS_CONN_FAIL);
writer.releaseLock(); reader.releaseLock();
return socket;
}
function parseSocksConfig(address) {
let [latter, former] = address.split("@").reverse();
let username, password, hostname, socksPort;
if (former) {
const formers = former.split(":");
if (formers.length !== 2) throw new Error(E_INVALID_SOCKS_ADDR);
[username, password] = formers;
}
const latters = latter.split(":");
socksPort = Number(latters.pop());
if (isNaN(socksPort)) throw new Error(E_INVALID_SOCKS_ADDR);
hostname = latters.join(":");
if (hostname.includes(":") && !/^\[.*\]$/.test(hostname)) throw new Error(E_INVALID_SOCKS_ADDR);
return { username, password, hostname, socksPort };
}
async function handleSubscriptionPage(request, user = null) {
if (!user) user = at;
const pageHtml = `
订阅中心
终端 v2.0
[ 选择客户端 ]
[ 系统状态 ]
[ 系统检测中... ]
Worker地区: 检测中...
检测方式: 检测中...
ProxyIP状态: 检测中...
当前使用IP: 检测中...
地区匹配: 检测中...
选择逻辑: 同地区 → 邻近地区 → 其他地区
`;
return new Response(pageHtml, {
status: 200,
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}
async function parseTrojanHeader(buffer, ut) {
const passwordToHash = tp || ut;
const sha224Password = await sha224Hash(passwordToHash);
if (buffer.byteLength < 56) {
return {
hasError: true,
message: "invalid " + atob('dHJvamFu') + " data - too short"
};
}
let crLfIndex = 56;
if (new Uint8Array(buffer.slice(56, 57))[0] !== 0x0d || new Uint8Array(buffer.slice(57, 58))[0] !== 0x0a) {
return {
hasError: true,
message: "invalid " + atob('dHJvamFu') + " header format (missing CR LF)"
};
}
const password = new TextDecoder().decode(buffer.slice(0, crLfIndex));
if (password !== sha224Password) {
return {
hasError: true,
message: "invalid " + atob('dHJvamFu') + " password"
};
}
const socks5DataBuffer = buffer.slice(crLfIndex + 2);
if (socks5DataBuffer.byteLength < 6) {
return {
hasError: true,
message: atob('aW52YWxpZCBTT0NLUzUgcmVxdWVzdCBkYXRh')
};
}
const view = new DataView(socks5DataBuffer);
const cmd = view.getUint8(0);
if (cmd !== 1) {
return {
hasError: true,
message: "unsupported command, only TCP (CONNECT) is allowed"
};
}
const atype = view.getUint8(1);
let addressLength = 0;
let addressIndex = 2;
let address = "";
switch (atype) {
case 1:
addressLength = 4;
address = new Uint8Array(
socks5DataBuffer.slice(addressIndex, addressIndex + addressLength)
).join(".");
break;
case 3:
addressLength = new Uint8Array(
socks5DataBuffer.slice(addressIndex, addressIndex + 1)
)[0];
addressIndex += 1;
address = new TextDecoder().decode(
socks5DataBuffer.slice(addressIndex, addressIndex + addressLength)
);
break;
case 4:
addressLength = 16;
const dataView = new DataView(socks5DataBuffer.slice(addressIndex, addressIndex + addressLength));
const ipv6 = [];
for (let i = 0; i < 8; i++) {
ipv6.push(dataView.getUint16(i * 2).toString(16));
}
address = ipv6.join(":");
break;
default:
return {
hasError: true,
message: `invalid addressType is ${atype}`
};
}
if (!address) {
return {
hasError: true,
message: `address is empty, addressType is ${atype}`
};
}
const portIndex = addressIndex + addressLength;
const portBuffer = socks5DataBuffer.slice(portIndex, portIndex + 2);
const portRemote = new DataView(portBuffer).getUint16(0);
return {
hasError: false,
addressRemote: address,
addressType: atype,
port: portRemote,
hostname: address,
rawClientData: socks5DataBuffer.slice(portIndex + 4)
};
}
async function sha224Hash(text) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
const K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
];
let H = [
0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4
];
const msgLen = data.length;
const bitLen = msgLen * 8;
const paddedLen = Math.ceil((msgLen + 9) / 64) * 64;
const padded = new Uint8Array(paddedLen);
padded.set(data);
padded[msgLen] = 0x80;
const view = new DataView(padded.buffer);
view.setUint32(paddedLen - 4, bitLen, false);
for (let chunk = 0; chunk < paddedLen; chunk += 64) {
const W = new Uint32Array(64);
for (let i = 0; i < 16; i++) {
W[i] = view.getUint32(chunk + i * 4, false);
}
for (let i = 16; i < 64; i++) {
const s0 = rightRotate(W[i - 15], 7) ^ rightRotate(W[i - 15], 18) ^ (W[i - 15] >>> 3);
const s1 = rightRotate(W[i - 2], 17) ^ rightRotate(W[i - 2], 19) ^ (W[i - 2] >>> 10);
W[i] = (W[i - 16] + s0 + W[i - 7] + s1) >>> 0;
}
let [a, b, c, d, e, f, g, h] = H;
for (let i = 0; i < 64; i++) {
const S1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25);
const ch = (e & f) ^ (~e & g);
const temp1 = (h + S1 + ch + K[i] + W[i]) >>> 0;
const S0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22);
const maj = (a & b) ^ (a & c) ^ (b & c);
const temp2 = (S0 + maj) >>> 0;
h = g;
g = f;
f = e;
e = (d + temp1) >>> 0;
d = c;
c = b;
b = a;
a = (temp1 + temp2) >>> 0;
}
H[0] = (H[0] + a) >>> 0;
H[1] = (H[1] + b) >>> 0;
H[2] = (H[2] + c) >>> 0;
H[3] = (H[3] + d) >>> 0;
H[4] = (H[4] + e) >>> 0;
H[5] = (H[5] + f) >>> 0;
H[6] = (H[6] + g) >>> 0;
H[7] = (H[7] + h) >>> 0;
}
const result = [];
for (let i = 0; i < 7; i++) {
result.push(
((H[i] >>> 24) & 0xff).toString(16).padStart(2, '0'),
((H[i] >>> 16) & 0xff).toString(16).padStart(2, '0'),
((H[i] >>> 8) & 0xff).toString(16).padStart(2, '0'),
(H[i] & 0xff).toString(16).padStart(2, '0')
);
}
return result.join('');
}
function rightRotate(value, amount) {
return (value >>> amount) | (value << (32 - amount));
}
let ACTIVE_CONNECTIONS = 0;
const XHTTP_BUFFER_SIZE = 128 * 1024;
const CONNECT_TIMEOUT_MS = 5000;
const IDLE_TIMEOUT_MS = 45000;
const MAX_RETRIES = 2;
const MAX_CONCURRENT = 32;
function xhttp_sleep(ms) {
return new Promise((r) => setTimeout(r, ms));
}
function validate_uuid_xhttp(id, uuid) {
for (let index = 0; index < 16; index++) {
if (id[index] !== uuid[index]) {
return false;
}
}
return true;
}
class XhttpCounter {
#total
constructor() {
this.#total = 0;
}
get() {
return this.#total;
}
add(size) {
this.#total += size;
}
}
function concat_typed_arrays(first, ...args) {
let len = first.length;
for (let a of args) {
len += a.length;
}
const r = new first.constructor(len);
r.set(first, 0);
len = first.length;
for (let a of args) {
r.set(a, len);
len += a.length;
}
return r;
}
function parse_uuid_xhttp(uuid) {
uuid = uuid.replaceAll('-', '');
const r = [];
for (let index = 0; index < 16; index++) {
const v = parseInt(uuid.substr(index * 2, 2), 16);
r.push(v);
}
return r;
}
function get_xhttp_buffer(size) {
return new Uint8Array(new ArrayBuffer(size || XHTTP_BUFFER_SIZE));
}
async function read_xhttp_header(readable, uuid_str) {
const reader = readable.getReader({ mode: 'byob' });
try {
let r = await reader.readAtLeast(1 + 16 + 1, get_xhttp_buffer());
let rlen = 0;
let idx = 0;
let cache = r.value;
rlen += r.value.length;
const version = cache[0];
const id = cache.slice(1, 1 + 16);
const uuid = parse_uuid_xhttp(uuid_str);
if (!validate_uuid_xhttp(id, uuid)) {
return `invalid UUID`;
}
const pb_len = cache[1 + 16];
const addr_plus1 = 1 + 16 + 1 + pb_len + 1 + 2 + 1;
if (addr_plus1 + 1 > rlen) {
if (r.done) {
return `header too short`;
}
idx = addr_plus1 + 1 - rlen;
r = await reader.readAtLeast(idx, get_xhttp_buffer());
rlen += r.value.length;
cache = concat_typed_arrays(cache, r.value);
}
const cmd = cache[1 + 16 + 1 + pb_len];
if (cmd !== 1) {
return `unsupported command: ${cmd}`;
}
const port = (cache[addr_plus1 - 1 - 2] << 8) + cache[addr_plus1 - 1 - 1];
const atype = cache[addr_plus1 - 1];
let header_len = -1;
if (atype === ADDRESS_TYPE_IPV4) {
header_len = addr_plus1 + 4;
} else if (atype === ADDRESS_TYPE_IPV6) {
header_len = addr_plus1 + 16;
} else if (atype === ADDRESS_TYPE_URL) {
header_len = addr_plus1 + 1 + cache[addr_plus1];
}
if (header_len < 0) {
return 'read address type failed';
}
idx = header_len - rlen;
if (idx > 0) {
if (r.done) {
return `read address failed`;
}
r = await reader.readAtLeast(idx, get_xhttp_buffer());
rlen += r.value.length;
cache = concat_typed_arrays(cache, r.value);
}
let hostname = '';
idx = addr_plus1;
switch (atype) {
case ADDRESS_TYPE_IPV4:
hostname = cache.slice(idx, idx + 4).join('.');
break;
case ADDRESS_TYPE_URL:
hostname = new TextDecoder().decode(
cache.slice(idx + 1, idx + 1 + cache[idx]),
);
break;
case ADDRESS_TYPE_IPV6:
hostname = cache
.slice(idx, idx + 16)
.reduce(
(s, b2, i2, a) =>
i2 % 2
? s.concat(((a[i2 - 1] << 8) + b2).toString(16))
: s,
[],
)
.join(':');
break;
}
if (hostname.length < 1) {
return 'failed to parse hostname';
}
const data = cache.slice(header_len);
return {
hostname,
port,
data,
resp: new Uint8Array([version, 0]),
reader,
done: r.done,
};
} catch (error) {
try { reader.releaseLock(); } catch (_) {}
throw error;
}
}
async function upload_to_remote_xhttp(counter, writer, httpx) {
async function inner_upload(d) {
if (!d || d.length === 0) {
return;
}
counter.add(d.length);
try {
await writer.write(d);
} catch (error) {
throw error;
}
}
try {
await inner_upload(httpx.data);
let chunkCount = 0;
while (!httpx.done) {
const r = await httpx.reader.read(get_xhttp_buffer());
if (r.done) break;
await inner_upload(r.value);
httpx.done = r.done;
chunkCount++;
if (chunkCount % 10 === 0) {
await xhttp_sleep(0);
}
if (!r.value || r.value.length === 0) {
await xhttp_sleep(2);
}
}
} catch (error) {
throw error;
}
}
function create_xhttp_uploader(httpx, writable) {
const counter = new XhttpCounter();
const writer = writable.getWriter();
const done = (async () => {
try {
await upload_to_remote_xhttp(counter, writer, httpx);
} catch (error) {
throw error;
} finally {
try {
await writer.close();
} catch (error) {
}
}
})();
return {
counter,
done,
abort: () => {
try { writer.abort(); } catch (_) {}
}
};
}
function create_xhttp_downloader(resp, remote_readable) {
const counter = new XhttpCounter();
let stream;
const done = new Promise((resolve, reject) => {
stream = new TransformStream(
{
start(controller) {
counter.add(resp.length);
controller.enqueue(resp);
},
transform(chunk, controller) {
counter.add(chunk.length);
controller.enqueue(chunk);
},
cancel(reason) {
reject(`download cancelled: ${reason}`);
},
},
null,
new ByteLengthQueuingStrategy({ highWaterMark: XHTTP_BUFFER_SIZE }),
);
let lastActivity = Date.now();
const idleTimer = setInterval(() => {
if (Date.now() - lastActivity > IDLE_TIMEOUT_MS) {
try {
stream.writable.abort?.('idle timeout');
} catch (_) {}
clearInterval(idleTimer);
reject('idle timeout');
}
}, 5000);
const reader = remote_readable.getReader();
const writer = stream.writable.getWriter();
;(async () => {
try {
let chunkCount = 0;
while (true) {
const r = await reader.read();
if (r.done) {
break;
}
lastActivity = Date.now();
await writer.write(r.value);
chunkCount++;
if (chunkCount % 5 === 0) {
await xhttp_sleep(0);
}
}
await writer.close();
resolve();
} catch (err) {
reject(err);
} finally {
try {
reader.releaseLock();
} catch (_) {}
try {
writer.releaseLock();
} catch (_) {}
clearInterval(idleTimer);
}
})();
});
return {
readable: stream.readable,
counter,
done,
abort: () => {
try { stream.readable.cancel(); } catch (_) {}
try { stream.writable.abort(); } catch (_) {}
}
};
}
async function connect_to_remote_xhttp(httpx, ...remotes) {
let attempt = 0;
let lastErr;
const connectionList = [httpx.hostname, ...remotes.filter(r => r && r !== httpx.hostname)];
for (const hostname of connectionList) {
if (!hostname) continue;
attempt = 0;
while (attempt < MAX_RETRIES) {
attempt++;
try {
const remote = connect({ hostname, port: httpx.port });
const timeoutPromise = xhttp_sleep(CONNECT_TIMEOUT_MS).then(() => {
throw new Error(atob('Y29ubmVjdCB0aW1lb3V0'));
});
await Promise.race([remote.opened, timeoutPromise]);
const uploader = create_xhttp_uploader(httpx, remote.writable);
const downloader = create_xhttp_downloader(httpx.resp, remote.readable);
return {
downloader,
uploader,
close: () => {
try { remote.close(); } catch (_) {}
}
};
} catch (err) {
lastErr = err;
if (attempt < MAX_RETRIES) {
await xhttp_sleep(500 * attempt);
}
}
}
}
return null;
}
async function handle_xhttp_client(body, uuid) {
if (ACTIVE_CONNECTIONS >= MAX_CONCURRENT) {
return new Response('Too many connections', { status: 429 });
}
ACTIVE_CONNECTIONS++;
let cleaned = false;
const cleanup = () => {
if (!cleaned) {
ACTIVE_CONNECTIONS = Math.max(0, ACTIVE_CONNECTIONS - 1);
cleaned = true;
}
};
try {
const httpx = await read_xhttp_header(body, uuid);
if (typeof httpx !== 'object' || !httpx) {
return null;
}
const remoteConnection = await connect_to_remote_xhttp(httpx, fallbackAddress, '13.230.34.30');
if (remoteConnection === null) {
return null;
}
const connectionClosed = Promise.race([
(async () => {
try {
await remoteConnection.downloader.done;
} catch (err) {
}
})(),
(async () => {
try {
await remoteConnection.uploader.done;
} catch (err) {
}
})(),
xhttp_sleep(IDLE_TIMEOUT_MS).then(() => {
})
]).finally(() => {
try { remoteConnection.close(); } catch (_) {}
try { remoteConnection.downloader.abort(); } catch (_) {}
try { remoteConnection.uploader.abort(); } catch (_) {}
cleanup();
});
return {
readable: remoteConnection.downloader.readable,
closed: connectionClosed
};
} catch (error) {
cleanup();
return null;
}
}
async function handleXhttpPost(request) {
try {
return await handle_xhttp_client(request.body, at);
} catch (err) {
return null;
}
}
function base64ToArray(b64Str) {
if (!b64Str) return { error: null };
try { b64Str = b64Str.replace(/-/g, '+').replace(/_/g, '/'); return { earlyData: Uint8Array.from(atob(b64Str), (c) => c.charCodeAt(0)).buffer, error: null }; }
catch (error) { return { error }; }
}
function closeSocketQuietly(socket) { try { if (socket.readyState === 1 || socket.readyState === 2) socket.close(); } catch (error) {} }
const hexTable = Array.from({ length: 256 }, (v, i) => (i + 256).toString(16).slice(1));
function formatIdentifier(arr, offset = 0) {
const id = (hexTable[arr[offset]]+hexTable[arr[offset+1]]+hexTable[arr[offset+2]]+hexTable[arr[offset+3]]+"-"+hexTable[arr[offset+4]]+hexTable[arr[offset+5]]+"-"+hexTable[arr[offset+6]]+hexTable[arr[offset+7]]+"-"+hexTable[arr[offset+8]]+hexTable[arr[offset+9]]+"-"+hexTable[arr[offset+10]]+hexTable[arr[offset+11]]+hexTable[arr[offset+12]]+hexTable[arr[offset+13]]+hexTable[arr[offset+14]]+hexTable[arr[offset+15]]).toLowerCase();
if (!isValidFormat(id)) throw new TypeError(E_INVALID_ID_STR);
return id;
}
async function fetchAndParseNewIPs() {
const url = piu || "https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt";
try {
const response = await fetch(url);
if (!response.ok) return [];
const text = await response.text();
const results = [];
const lines = text.trim().replace(/\r/g, "").split('\n');
const regex = /^([^:]+):(\d+)#(.*)$/;
for (const line of lines) {
const trimmedLine = line.trim();
if (!trimmedLine) continue;
const match = trimmedLine.match(regex);
if (match) {
results.push({
ip: match[1],
port: parseInt(match[2], 10),
name: match[3].trim() || match[1]
});
}
}
return results;
} catch (error) {
return [];
}
}
function generateLinksFromNewIPs(list, user, workerDomain) {
const CF_HTTP_PORTS = [80, 8080, 8880, 2052, 2082, 2086, 2095];
const CF_HTTPS_PORTS = [443, 2053, 2083, 2087, 2096, 8443];
const links = [];
const wsPath = encodeURIComponent('/');
const proto = atob('dmxlc3M=');
list.forEach(item => {
const nodeName = item.name.replace(/\s/g, '_');
const port = item.port;
if (CF_HTTPS_PORTS.includes(port)) {
const wsNodeName = `${nodeName}-${port}-WS-TLS`;
const link = `${proto}://${user}@${item.ip}:${port}?encryption=none&security=tls&sni=${workerDomain}&fp=chrome&type=ws&host=${workerDomain}&path=${wsPath}#${encodeURIComponent(wsNodeName)}`;
links.push(link);
} else if (CF_HTTP_PORTS.includes(port)) {
if (!disableNonTLS) {
const wsNodeName = `${nodeName}-${port}-WS`;
const link = `${proto}://${user}@${item.ip}:${port}?encryption=none&security=none&type=ws&host=${workerDomain}&path=${wsPath}#${encodeURIComponent(wsNodeName)}`;
links.push(link);
}
} else {
const wsNodeName = `${nodeName}-${port}-WS-TLS`;
const link = `${proto}://${user}@${item.ip}:${port}?encryption=none&security=tls&sni=${workerDomain}&fp=chrome&type=ws&host=${workerDomain}&path=${wsPath}#${encodeURIComponent(wsNodeName)}`;
links.push(link);
}
});
return links;
}
function generateXhttpLinksFromSource(list, user, workerDomain) {
const links = [];
const nodePath = user.substring(0, 8);
list.forEach(item => {
const nodeNameBase = item.isp.replace(/\s/g, '_');
const safeIP = item.ip.includes(':') ? `[${item.ip}]` : item.ip;
const port = item.port || 443;
const wsNodeName = `${nodeNameBase}-${port}-xhttp`;
const params = new URLSearchParams({
encryption: 'none',
security: 'tls',
sni: workerDomain,
fp: 'chrome',
allowInsecure: '1',
type: 'xhttp',
host: workerDomain,
path: `/${nodePath}`,
mode: 'stream-one'
});
links.push(`vless://${user}@${safeIP}:${port}?${params.toString()}#${encodeURIComponent(wsNodeName)}`);
});
return links;
}
async function generateTrojanLinksFromNewIPs(list, user, workerDomain) {
const CF_HTTP_PORTS = [80, 8080, 8880, 2052, 2082, 2086, 2095];
const CF_HTTPS_PORTS = [443, 2053, 2083, 2087, 2096, 8443];
const links = [];
const wsPath = encodeURIComponent('/');
const password = tp || user;
list.forEach(item => {
const nodeName = item.name.replace(/\s/g, '_');
const port = item.port;
if (CF_HTTPS_PORTS.includes(port)) {
const wsNodeName = `${nodeName}-${port}-${atob('VHJvamFu')}-WS-TLS`;
const link = `${atob('dHJvamFuOi8v')}${password}@${item.ip}:${port}?security=tls&sni=${workerDomain}&fp=chrome&type=ws&host=${workerDomain}&path=${wsPath}#${encodeURIComponent(wsNodeName)}`;
links.push(link);
} else if (CF_HTTP_PORTS.includes(port)) {
if (!disableNonTLS) {
const wsNodeName = `${nodeName}-${port}-${atob('VHJvamFu')}-WS`;
const link = `${atob('dHJvamFuOi8v')}${password}@${item.ip}:${port}?security=none&type=ws&host=${workerDomain}&path=${wsPath}#${encodeURIComponent(wsNodeName)}`;
links.push(link);
}
} else {
const wsNodeName = `${nodeName}-${port}-${atob('VHJvamFu')}-WS-TLS`;
const link = `${atob('dHJvamFuOi8v')}${password}@${item.ip}:${port}?security=tls&sni=${workerDomain}&fp=chrome&type=ws&host=${workerDomain}&path=${wsPath}#${encodeURIComponent(wsNodeName)}`;
links.push(link);
}
});
return links;
}
async function handleConfigAPI(request) {
if (request.method === 'GET') {
if (!kvStore) {
return new Response(JSON.stringify({
error: 'KV存储未配置',
kvEnabled: false
}), {
status: 503,
headers: { 'Content-Type': 'application/json' }
});
}
return new Response(JSON.stringify({
...kvConfig,
kvEnabled: true
}), {
headers: { 'Content-Type': 'application/json' }
});
} else if (request.method === 'POST') {
if (!kvStore) {
return new Response(JSON.stringify({
success: false,
message: 'KV存储未配置,无法保存配置'
}), {
status: 503,
headers: { 'Content-Type': 'application/json' }
});
}
try {
const newConfig = await request.json();
for (const [key, value] of Object.entries(newConfig)) {
if (value === '' || value === null || value === undefined) {
delete kvConfig[key];
} else {
kvConfig[key] = value;
}
}
await saveKVConfig();
updateConfigVariables();
if (newConfig.yx !== undefined) {
updateCustomPreferredFromYx();
}
const newPreferredIPsURL = getConfigValue('yxURL', '') || 'https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt';
const defaultURL = 'https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt';
if (newPreferredIPsURL !== defaultURL) {
directDomains.length = 0;
customPreferredIPs = [];
customPreferredDomains = [];
} else {
backupIPs = [
{ domain: 'ProxyIP.US.CMLiussss.net', region: 'US', regionCode: 'US', port: 443 },
{ domain: 'ProxyIP.SG.CMLiussss.net', region: 'SG', regionCode: 'SG', port: 443 },
{ domain: 'ProxyIP.JP.CMLiussss.net', region: 'JP', regionCode: 'JP', port: 443 },
{ domain: 'ProxyIP.HK.CMLiussss.net', region: 'HK', regionCode: 'HK', port: 443 },
{ domain: 'ProxyIP.KR.CMLiussss.net', region: 'KR', regionCode: 'KR', port: 443 },
{ domain: 'ProxyIP.DE.CMLiussss.net', region: 'DE', regionCode: 'DE', port: 443 },
{ domain: 'ProxyIP.SE.CMLiussss.net', region: 'SE', regionCode: 'SE', port: 443 },
{ domain: 'ProxyIP.NL.CMLiussss.net', region: 'NL', regionCode: 'NL', port: 443 },
{ domain: 'ProxyIP.FI.CMLiussss.net', region: 'FI', regionCode: 'FI', port: 443 },
{ domain: 'ProxyIP.GB.CMLiussss.net', region: 'GB', regionCode: 'GB', port: 443 },
{ domain: 'ProxyIP.Oracle.cmliussss.net', region: 'Oracle', regionCode: 'Oracle', port: 443 },
{ domain: 'ProxyIP.DigitalOcean.CMLiussss.net', region: 'DigitalOcean', regionCode: 'DigitalOcean', port: 443 },
{ domain: 'ProxyIP.Vultr.CMLiussss.net', region: 'Vultr', regionCode: 'Vultr', port: 443 },
{ domain: 'ProxyIP.Multacom.CMLiussss.net', region: 'Multacom', regionCode: 'Multacom', port: 443 }
];
directDomains.length = 0;
directDomains.push(
{ name: "cloudflare.182682.xyz", domain: "cloudflare.182682.xyz" },
{ name: "speed.marisalnc.com", domain: "speed.marisalnc.com" },
{ domain: "freeyx.cloudflare88.eu.org" },
{ domain: "bestcf.top" },
{ domain: "cdn.2020111.xyz" },
{ domain: "cfip.cfcdn.vip" },
{ domain: "cf.0sm.com" },
{ domain: "cf.090227.xyz" },
{ domain: "cf.zhetengsha.eu.org" },
{ domain: "cloudflare.9jy.cc" },
{ domain: "cf.zerone-cdn.pp.ua" },
{ domain: "cfip.1323123.xyz" },
{ domain: "cnamefuckxxs.yuchen.icu" },
{ domain: "cloudflare-ip.mofashi.ltd" },
{ domain: "115155.xyz" },
{ domain: "cname.xirancdn.us" },
{ domain: "f3058171cad.002404.xyz" },
{ domain: "8.889288.xyz" },
{ domain: "cdn.tzpro.xyz" },
{ domain: "cf.877771.xyz" },
{ domain: "xn--b6gac.eu.org" }
);
}
return new Response(JSON.stringify({
success: true,
message: '配置已保存',
config: kvConfig
}), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({
success: false,
message: '保存配置失败: ' + error.message
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
return new Response(JSON.stringify({ error: 'Method not allowed' }), {
status: 405,
headers: { 'Content-Type': 'application/json' }
});
}
async function handlePreferredIPsAPI(request) {
if (!kvStore) {
return new Response(JSON.stringify({
success: false,
error: 'KV存储未配置',
message: '需要配置KV存储才能使用此功能'
}), {
status: 503,
headers: { 'Content-Type': 'application/json' }
});
}
const ae = getConfigValue('ae', '') === 'yes';
if (!ae) {
return new Response(JSON.stringify({
success: false,
error: 'API功能未启用',
message: '出于安全考虑,优选IP API功能默认关闭。请在配置管理页面开启"允许API管理"选项后使用。'
}), {
status: 403,
headers: { 'Content-Type': 'application/json' }
});
}
try {
if (request.method === 'GET') {
const yxValue = getConfigValue('yx', '');
const pi = parseYxToArray(yxValue);
return new Response(JSON.stringify({
success: true,
count: pi.length,
data: pi
}), {
headers: { 'Content-Type': 'application/json' }
});
} else if (request.method === 'POST') {
const body = await request.json();
const ipsToAdd = Array.isArray(body) ? body : [body];
if (ipsToAdd.length === 0) {
return new Response(JSON.stringify({
success: false,
error: '请求数据为空',
message: '请提供IP数据'
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
const yxValue = getConfigValue('yx', '');
let pi = parseYxToArray(yxValue);
const addedIPs = [];
const skippedIPs = [];
const errors = [];
for (const item of ipsToAdd) {
if (!item.ip) {
errors.push({ ip: '未知', reason: 'IP地址是必需的' });
continue;
}
const port = item.port || 443;
const name = item.name || `API优选-${item.ip}:${port}`;
if (!isValidIP(item.ip) && !isValidDomain(item.ip)) {
errors.push({ ip: item.ip, reason: '无效的IP或域名格式' });
continue;
}
const exists = pi.some(existItem =>
existItem.ip === item.ip && existItem.port === port
);
if (exists) {
skippedIPs.push({ ip: item.ip, port: port, reason: '已存在' });
continue;
}
const newIP = {
ip: item.ip,
port: port,
name: name,
addedAt: new Date().toISOString()
};
pi.push(newIP);
addedIPs.push(newIP);
}
if (addedIPs.length > 0) {
const newYxValue = arrayToYx(pi);
await setConfigValue('yx', newYxValue);
updateCustomPreferredFromYx();
}
return new Response(JSON.stringify({
success: addedIPs.length > 0,
message: `成功添加 ${addedIPs.length} 个IP`,
added: addedIPs.length,
skipped: skippedIPs.length,
errors: errors.length,
data: {
addedIPs: addedIPs,
skippedIPs: skippedIPs.length > 0 ? skippedIPs : undefined,
errors: errors.length > 0 ? errors : undefined
}
}), {
headers: { 'Content-Type': 'application/json' }
});
} else if (request.method === 'DELETE') {
const body = await request.json();
if (body.all === true) {
const yxValue = getConfigValue('yx', '');
const pi = parseYxToArray(yxValue);
const deletedCount = pi.length;
await setConfigValue('yx', '');
updateCustomPreferredFromYx();
return new Response(JSON.stringify({
success: true,
message: `已清空所有优选IP,共删除 ${deletedCount} 个`,
deletedCount: deletedCount
}), {
headers: { 'Content-Type': 'application/json' }
});
}
if (!body.ip) {
return new Response(JSON.stringify({
success: false,
error: 'IP地址是必需的',
message: '请提供要删除的ip字段,或使用 {"all": true} 清空所有'
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
const port = body.port || 443;
const yxValue = getConfigValue('yx', '');
let pi = parseYxToArray(yxValue);
const initialLength = pi.length;
const filteredIPs = pi.filter(item =>
!(item.ip === body.ip && item.port === port)
);
if (filteredIPs.length === initialLength) {
return new Response(JSON.stringify({
success: false,
error: '优选IP不存在',
message: `${body.ip}:${port} 未找到`
}), {
status: 404,
headers: { 'Content-Type': 'application/json' }
});
}
const newYxValue = arrayToYx(filteredIPs);
await setConfigValue('yx', newYxValue);
updateCustomPreferredFromYx();
return new Response(JSON.stringify({
success: true,
message: '优选IP已删除',
deleted: { ip: body.ip, port: port }
}), {
headers: { 'Content-Type': 'application/json' }
});
} else {
return new Response(JSON.stringify({
success: false,
error: '不支持的请求方法',
message: '支持的方法: GET, POST, DELETE'
}), {
status: 405,
headers: { 'Content-Type': 'application/json' }
});
}
} catch (error) {
return new Response(JSON.stringify({
success: false,
error: '处理请求失败',
message: error.message
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
function updateConfigVariables() {
const manualRegion = getConfigValue('wk', '');
if (manualRegion && manualRegion.trim()) {
manualWorkerRegion = manualRegion.trim().toUpperCase();
currentWorkerRegion = manualWorkerRegion;
} else {
const ci = getConfigValue('p', '');
if (ci && ci.trim()) {
currentWorkerRegion = 'CUSTOM';
} else {
manualWorkerRegion = '';
}
}
const regionMatchingControl = getConfigValue('rm', '');
if (regionMatchingControl && regionMatchingControl.toLowerCase() === 'no') {
enableRegionMatching = false;
} else {
enableRegionMatching = true;
}
const vlessControl = getConfigValue('ev', '');
if (vlessControl !== undefined && vlessControl !== '') {
ev = vlessControl === 'yes' || vlessControl === true || vlessControl === 'true';
}
const tjControl = getConfigValue('et', '');
if (tjControl !== undefined && tjControl !== '') {
et = tjControl === 'yes' || tjControl === true || tjControl === 'true';
}
tp = getConfigValue('tp', '') || '';
const xhttpControl = getConfigValue('ex', '');
if (xhttpControl !== undefined && xhttpControl !== '') {
ex = xhttpControl === 'yes' || xhttpControl === true || xhttpControl === 'true';
}
if (!ev && !et && !ex) {
ev = true;
}
scu = getConfigValue('scu', '') || 'https://url.v1.mk/sub';
const preferredDomainsControl = getConfigValue('epd', '');
if (preferredDomainsControl !== undefined && preferredDomainsControl !== '') {
epd = preferredDomainsControl !== 'no' && preferredDomainsControl !== false && preferredDomainsControl !== 'false';
}
const preferredIPsControl = getConfigValue('epi', '');
if (preferredIPsControl !== undefined && preferredIPsControl !== '') {
epi = preferredIPsControl !== 'no' && preferredIPsControl !== false && preferredIPsControl !== 'false';
}
const githubIPsControl = getConfigValue('egi', '');
if (githubIPsControl !== undefined && githubIPsControl !== '') {
egi = githubIPsControl !== 'no' && githubIPsControl !== false && githubIPsControl !== 'false';
}
cp = getConfigValue('d', '') || '';
piu = getConfigValue('yxURL', '') || 'https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt';
const envFallback = getConfigValue('p', '');
if (envFallback) {
const fallbackValue = envFallback.toLowerCase();
if (fallbackValue.includes(']:')) {
const lastColonIndex = fallbackValue.lastIndexOf(':');
fallbackPort = fallbackValue.slice(lastColonIndex + 1);
fallbackAddress = fallbackValue.slice(0, lastColonIndex);
} else if (!fallbackValue.includes(']:') && !fallbackValue.includes(']')) {
[fallbackAddress, fallbackPort = '443'] = fallbackValue.split(':');
} else {
fallbackAddress = fallbackValue;
fallbackPort = '443';
}
} else {
fallbackAddress = '';
fallbackPort = '443';
}
socks5Config = getConfigValue('s', '') || '';
if (socks5Config) {
try {
parsedSocks5Config = parseSocksConfig(socks5Config);
isSocksEnabled = true;
} catch (err) {
isSocksEnabled = false;
}
} else {
isSocksEnabled = false;
}
const yxbyControl = getConfigValue('yxby', '');
if (yxbyControl && yxbyControl.toLowerCase() === 'yes') {
disablePreferred = true;
} else {
disablePreferred = false;
}
const defaultURL = 'https://raw.githubusercontent.com/qwer-search/bestip/refs/heads/main/kejilandbestip.txt';
if (piu !== defaultURL) {
directDomains.length = 0;
customPreferredIPs = [];
customPreferredDomains = [];
}
}
function updateCustomPreferredFromYx() {
const yxValue = getConfigValue('yx', '');
if (yxValue) {
try {
const preferredList = yxValue.split(',').map(item => item.trim()).filter(item => item);
customPreferredIPs = [];
customPreferredDomains = [];
preferredList.forEach(item => {
let nodeName = '';
let addressPart = item;
if (item.includes('#')) {
const parts = item.split('#');
addressPart = parts[0].trim();
nodeName = parts[1].trim();
}
const { address, port } = parseAddressAndPort(addressPart);
if (!nodeName) {
nodeName = '自定义优选-' + address + (port ? ':' + port : '');
}
if (isValidIP(address)) {
customPreferredIPs.push({
ip: address,
port: port,
isp: nodeName
});
} else {
customPreferredDomains.push({
domain: address,
port: port,
name: nodeName
});
}
});
} catch (err) {
customPreferredIPs = [];
customPreferredDomains = [];
}
} else {
customPreferredIPs = [];
customPreferredDomains = [];
}
}
function parseYxToArray(yxValue) {
if (!yxValue || !yxValue.trim()) return [];
const items = yxValue.split(',').map(item => item.trim()).filter(item => item);
const result = [];
for (const item of items) {
let nodeName = '';
let addressPart = item;
if (item.includes('#')) {
const parts = item.split('#');
addressPart = parts[0].trim();
nodeName = parts[1].trim();
}
const { address, port } = parseAddressAndPort(addressPart);
if (!nodeName) {
nodeName = address + (port ? ':' + port : '');
}
result.push({
ip: address,
port: port || 443,
name: nodeName,
addedAt: new Date().toISOString()
});
}
return result;
}
function arrayToYx(array) {
if (!array || array.length === 0) return '';
return array.map(item => {
const port = item.port || 443;
return `${item.ip}:${port}#${item.name}`;
}).join(',');
}
function isValidDomain(domain) {
const domainRegex = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
return domainRegex.test(domain);
}
|