multiple domains handling

This commit is contained in:
Flowseal
2026-04-09 23:12:17 +03:00
parent dd666489e3
commit dd09f24449
8 changed files with 211 additions and 68 deletions

View File

@@ -161,22 +161,33 @@ async def _cfproxy_fallback(reader, writer, relay_init, label,
clt_decryptor=None, clt_encryptor=None,
tg_encryptor=None, tg_decryptor=None,
splitter=None):
domain = f'kws{dc}.{proxy_config.fallback_cfproxy_domain}'
media_tag = ' media' if is_media else ''
ws = None
log.info("[%s] DC%d%s -> CF proxy wss://%s/apiws",
label, dc, media_tag, domain)
try:
ws = await RawWebSocket.connect(domain, domain,
timeout=10.0)
except Exception as exc:
log.warning("[%s] DC%d%s CF proxy %s failed: %s",
label, dc, media_tag, domain, exc)
active = proxy_config.active_cfproxy_domain
others = [d for d in proxy_config.cfproxy_domains if d != active]
ws = None
chosen_domain = None
for base_domain in ([active] + others):
domain = f'kws{dc}.{base_domain}'
log.info("[%s] DC%d%s -> CF proxy wss",
label, dc, media_tag)
try:
ws = await RawWebSocket.connect(domain, domain, timeout=10.0)
chosen_domain = base_domain
break
except Exception as exc:
log.warning("[%s] DC%d%s CF proxy failed: %s",
label, dc, media_tag, exc)
if ws is None:
return False
if chosen_domain and chosen_domain != proxy_config.active_cfproxy_domain:
log.info("[%s] Switching active CF domain", label)
proxy_config.active_cfproxy_domain = chosen_domain
stats.connections_cfproxy += 1
await ws.send(relay_init)
await bridge_ws_reencrypt(reader, writer, ws, label,

View File

@@ -1,8 +1,36 @@
import logging
import os
import random
import socket as _socket
import threading
from typing import Dict, List
from dataclasses import dataclass, field
from typing import Dict, List
from urllib.request import Request, urlopen
log = logging.getLogger('tg-mtproto-proxy')
CFPROXY_DOMAINS_URL = (
"https://raw.githubusercontent.com/Flowseal/tg-ws-proxy/main"
"/.github/cfproxy-domains.txt"
)
_CFPROXY_ENC: List[str] = ['virkgj.com']
_S = ''.join(chr(c) for c in (46, 99, 111, 46, 117, 107))
def _dd(s: str) -> str:
"""Only for decoding CF proxy domains"""
if not s[-4:] == '.com':
return s
p, n = s[:-4], sum(c.isalpha() for c in s[:-4])
return ''.join(
chr((ord(c) - (97 if c > '`' else 65) - n) % 26 + (97 if c > '`' else 65))
if c.isalpha() else c for c in p
) + _S
CFPROXY_DEFAULT_DOMAINS: List[str] = [_dd(d) for d in _CFPROXY_ENC]
@dataclass
@@ -15,12 +43,54 @@ class ProxyConfig:
pool_size: int = 4
fallback_cfproxy: bool = True
fallback_cfproxy_priority: bool = True
fallback_cfproxy_domain: str = 'pclead.co.uk'
cfproxy_user_domain: str = ''
cfproxy_domains: List[str] = field(default_factory=lambda: list(CFPROXY_DEFAULT_DOMAINS))
active_cfproxy_domain: str = field(default_factory=lambda: random.choice(CFPROXY_DEFAULT_DOMAINS))
proxy_config = ProxyConfig()
def _fetch_cfproxy_domain_list() -> List[str]:
try:
req = Request(CFPROXY_DOMAINS_URL, headers={'User-Agent': 'tg-ws-proxy'})
with urlopen(req, timeout=10) as resp:
text = resp.read().decode('utf-8', errors='replace')
encoded = [
line.strip() for line in text.splitlines()
if line.strip() and not line.startswith('#')
]
return [_dd(d) for d in encoded]
except Exception as exc:
log.warning("Failed to fetch CF proxy domain list: %s", exc)
return []
def refresh_cfproxy_domains() -> None:
if proxy_config.cfproxy_user_domain:
return
fetched = _fetch_cfproxy_domain_list()
if fetched:
seen = set()
pool = [d for d in fetched if not (d in seen or seen.add(d))]
log.info("CF proxy domain pool updated from GitHub (%d domains)", len(pool))
else:
pool = list(proxy_config.cfproxy_domains) or list(CFPROXY_DEFAULT_DOMAINS)
proxy_config.cfproxy_domains = pool
proxy_config.active_cfproxy_domain = random.choice(pool)
def start_cfproxy_domain_refresh() -> None:
threading.Thread(
target=refresh_cfproxy_domains,
daemon=True,
name='cfproxy-domains-refresh',
).start()
def parse_dc_ip_list(dc_ip_list: List[str]) -> Dict[int, str]:
dc_redirects: Dict[int, str] = {}
for entry in dc_ip_list:

View File

@@ -4,6 +4,7 @@ import os
import sys
import time
import struct
import random
import asyncio
import hashlib
import argparse
@@ -24,7 +25,7 @@ if __name__ == '__main__' and (__package__ is None or __package__ == ''):
from .utils import *
from .stats import stats
from .config import proxy_config, parse_dc_ip_list
from .config import proxy_config, parse_dc_ip_list, start_cfproxy_domain_refresh, CFPROXY_DEFAULT_DOMAINS
from .bridge import MsgSplitter, do_fallback, bridge_ws_reencrypt
from .raw_websocket import RawWebSocket, WsHandshakeError, set_sock_opts
@@ -445,6 +446,16 @@ async def _run(stop_event: Optional[asyncio.Event] = None):
ws_blacklist.clear()
dc_fail_until.clear()
if proxy_config.fallback_cfproxy:
user = proxy_config.cfproxy_user_domain
if user:
proxy_config.cfproxy_domains = [user]
proxy_config.active_cfproxy_domain = user
else:
proxy_config.cfproxy_domains = list(CFPROXY_DEFAULT_DOMAINS)
proxy_config.active_cfproxy_domain = random.choice(CFPROXY_DEFAULT_DOMAINS)
start_cfproxy_domain_refresh()
secret_bytes = bytes.fromhex(proxy_config.secret)
def client_cb(r, w):
@@ -472,8 +483,8 @@ async def _run(stop_event: Optional[asyncio.Event] = None):
log.info(" DC%d: %s", dc, ip)
if proxy_config.fallback_cfproxy:
prio = 'CF first' if proxy_config.fallback_cfproxy_priority else 'TCP first'
log.info(" CF proxy: %s (%s)",
proxy_config.fallback_cfproxy_domain, prio)
user_domain = "user" if proxy_config.cfproxy_user_domain else "auto"
log.info(" CF proxy: enabled (%s | %s)", prio, user_domain)
log.info("=" * 60)
log.info(" Connect link:")
log.info(" %s", tg_link)
@@ -557,10 +568,9 @@ def main():
help='Socket send/recv buffer size in KB (default 256)')
ap.add_argument('--pool-size', type=int, default=4, metavar='N',
help='WS connection pool size per DC (default 4, min 0)')
ap.add_argument('--cfproxy-domain', type=str, default='pclead.co.uk',
ap.add_argument('--cfproxy-domain', type=str, default='',
metavar='DOMAIN',
help='Cloudflare-proxied domain for WS fallback '
'(default: pclead.co.uk)')
help='User defined Cloudflare-proxied domain for WS fallback')
ap.add_argument('--no-cfproxy', action='store_true',
help='Disable Cloudflare proxy fallback')
ap.add_argument('--cfproxy-priority', type=bool, default=True,
@@ -598,7 +608,7 @@ def main():
proxy_config.pool_size = max(0, args.pool_size)
proxy_config.fallback_cfproxy = not args.no_cfproxy
proxy_config.fallback_cfproxy_priority = args.cfproxy_priority
proxy_config.fallback_cfproxy_domain = args.cfproxy_domain
proxy_config.cfproxy_user_domain = args.cfproxy_domain
log_level = logging.DEBUG if args.verbose else logging.INFO
log_fmt = logging.Formatter('%(asctime)s %(levelname)-5s %(message)s',