mirror of
https://github.com/Flowseal/tg-ws-proxy.git
synced 2026-07-08 21:10:27 +03:00
Поддержка тестового окружения Telegram (тестовые DC) (#1087)
This commit is contained in:
+8
-7
@@ -130,10 +130,11 @@ class MsgSplitter:
|
||||
|
||||
|
||||
async def do_fallback(reader, writer, relay_init, label,
|
||||
dc: int, is_media: bool, media_tag: str,
|
||||
dc: int, is_test_dc: bool, is_media: bool, media_tag: str,
|
||||
ctx: CryptoCtx, splitter=None):
|
||||
fallback_dst = DC_DEFAULT_IPS.get(dc)
|
||||
use_cf = proxy_config.fallback_cfproxy
|
||||
ip_table = DC_TEST_IPS if is_test_dc else DC_DEFAULT_IPS
|
||||
fallback_dst = ip_table.get(dc)
|
||||
use_cf = proxy_config.fallback_cfproxy and not is_test_dc
|
||||
worker_domains = proxy_config.cfproxy_worker_domains
|
||||
|
||||
methods: List[str] = []
|
||||
@@ -149,8 +150,8 @@ async def do_fallback(reader, writer, relay_init, label,
|
||||
if method == 'cf_worker' and fallback_dst:
|
||||
ok = await _cfproxy_worker_fallback(
|
||||
reader, writer, relay_init, label, ctx,
|
||||
dc=dc, is_media=is_media, fallback_dst=fallback_dst,
|
||||
splitter=splitter)
|
||||
dc=dc, is_test_dc=is_test_dc, is_media=is_media,
|
||||
fallback_dst=fallback_dst, splitter=splitter)
|
||||
if ok:
|
||||
return True
|
||||
elif method == 'cf':
|
||||
@@ -173,7 +174,7 @@ async def do_fallback(reader, writer, relay_init, label,
|
||||
|
||||
async def _cfproxy_worker_fallback(reader, writer, relay_init, label,
|
||||
ctx: CryptoCtx,
|
||||
dc: int, is_media: bool,
|
||||
dc: int, is_test_dc: bool, is_media: bool,
|
||||
fallback_dst: str,
|
||||
splitter=None):
|
||||
media_tag = ' media' if is_media else ''
|
||||
@@ -184,7 +185,7 @@ async def _cfproxy_worker_fallback(reader, writer, relay_init, label,
|
||||
random.shuffle(worker_domains)
|
||||
|
||||
for worker_domain in worker_domains:
|
||||
ws = await cf_worker_pool.get(dc, worker_domain, fallback_dst)
|
||||
ws = None if is_test_dc else await cf_worker_pool.get(dc, worker_domain, fallback_dst)
|
||||
if ws:
|
||||
log.info("[%s] DC%d%s -> CF worker pool hit for %s",
|
||||
label, dc, media_tag, fallback_dst)
|
||||
|
||||
@@ -72,6 +72,7 @@ class ProxyConfig:
|
||||
cfproxy_worker_domains: List[str] = field(default_factory=list)
|
||||
fake_tls_domain: str = ''
|
||||
proxy_protocol: bool = False
|
||||
force_test_dc: bool = False
|
||||
|
||||
|
||||
proxy_config = ProxyConfig()
|
||||
|
||||
+1
-1
@@ -213,4 +213,4 @@ class _CfWorkerPool:
|
||||
|
||||
|
||||
ws_pool = _WsPool()
|
||||
cf_worker_pool = _CfWorkerPool()
|
||||
cf_worker_pool = _CfWorkerPool()
|
||||
|
||||
+22
-9
@@ -280,6 +280,11 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
|
||||
dc, is_media, proto_tag, client_dec_prekey_iv = result
|
||||
|
||||
is_test_dc = proxy_config.force_test_dc or dc >= 10000
|
||||
if dc >= 10000:
|
||||
log.info("[%s] test DC%d -> DC%d", label, dc, dc - 10000)
|
||||
dc -= 10000
|
||||
|
||||
if proto_tag == PROTO_TAG_ABRIDGED:
|
||||
proto_int = PROTO_ABRIDGED_INT
|
||||
elif proto_tag == PROTO_TAG_INTERMEDIATE:
|
||||
@@ -295,14 +300,15 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
relay_init = _generate_relay_init(proto_tag, dc_idx)
|
||||
ctx = _build_crypto_ctx(client_dec_prekey_iv, secret, relay_init)
|
||||
|
||||
dc_key = f'{dc}{"m" if is_media else ""}'
|
||||
dc_key = f'{dc}{"t" if is_test_dc else ""}{"m" if is_media else ""}'
|
||||
media_tag = " media" if is_media else ""
|
||||
now = time.monotonic()
|
||||
ws_path = WS_PATH_TEST if is_test_dc else WS_PATH
|
||||
target = proxy_config.dc_redirects.get(dc)
|
||||
is_any_cf_fallback = proxy_config.fallback_cfproxy or proxy_config.cfproxy_worker_domains
|
||||
|
||||
# Fallback if DC not in config, if WS blacklisted for this DC/is_media or if connect to ip is timed out
|
||||
if (dc not in proxy_config.dc_redirects
|
||||
if (dc not in proxy_config.dc_redirects
|
||||
or dc_key in ws_blacklist
|
||||
or now < ip_fail_until.get(target, 0) and is_any_cf_fallback):
|
||||
|
||||
@@ -322,7 +328,7 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
pass
|
||||
ok = await do_fallback(
|
||||
clt_reader, clt_writer, relay_init, label,
|
||||
dc, is_media, media_tag,
|
||||
dc, is_test_dc, is_media, media_tag,
|
||||
ctx, splitter=splitter)
|
||||
if not ok:
|
||||
log.warning("[%s] DC%d%s no fallback available",
|
||||
@@ -338,7 +344,7 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
ws_timed_out = False
|
||||
all_redirects = True
|
||||
|
||||
ws = await ws_pool.get(dc, is_media, target, domains)
|
||||
ws = await ws_pool.get(dc, is_media, target, domains, path=ws_path) if not is_test_dc else None
|
||||
if ws:
|
||||
log.info("[%s] DC%d%s -> pool hit via %s",
|
||||
label, dc, media_tag, target)
|
||||
@@ -348,7 +354,7 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
label, dc, media_tag, domains[0])
|
||||
try:
|
||||
ws = await RawWebSocket.connect(target, domains[0],
|
||||
timeout=5.0,
|
||||
timeout=5.0, path=ws_path,
|
||||
sni="sprinthost.ru")
|
||||
except Exception as exc:
|
||||
stats.ws_errors += 1
|
||||
@@ -363,12 +369,13 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
ws_pool.fronting_until = 0.0
|
||||
else:
|
||||
for domain in domains:
|
||||
url = f'wss://{domain}/apiws'
|
||||
url = f'wss://{domain}{ws_path}'
|
||||
log.info("[%s] DC%d%s -> %s via %s",
|
||||
label, dc, media_tag, url, target)
|
||||
try:
|
||||
ws = await RawWebSocket.connect(target, domain,
|
||||
timeout=ws_timeout)
|
||||
timeout=ws_timeout,
|
||||
path=ws_path)
|
||||
all_redirects = False
|
||||
break
|
||||
except WsHandshakeError as exc:
|
||||
@@ -404,7 +411,7 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
label, dc, media_tag, domains[0])
|
||||
try:
|
||||
ws = await RawWebSocket.connect(target, domains[0],
|
||||
timeout=5.0,
|
||||
timeout=5.0, path=ws_path,
|
||||
sni="sprinthost.ru")
|
||||
except Exception as exc:
|
||||
stats.ws_errors += 1
|
||||
@@ -440,7 +447,7 @@ async def _handle_client(reader, writer, secret: bytes):
|
||||
pass
|
||||
ok = await do_fallback(
|
||||
clt_reader, clt_writer, relay_init, label,
|
||||
dc, is_media, media_tag,
|
||||
dc, is_test_dc, is_media, media_tag,
|
||||
ctx, splitter=splitter_fb)
|
||||
if ok:
|
||||
log.info("[%s] DC%d%s fallback closed",
|
||||
@@ -703,6 +710,11 @@ def main():
|
||||
metavar='DOMAIN',
|
||||
help='Enable Fake TLS (ee-secret) masking with the given '
|
||||
'SNI domain, e.g. example.com')
|
||||
ap.add_argument('--force-test-dc', action='store_true',
|
||||
help='Force ALL traffic to Telegram TEST datacenters. '
|
||||
'Not needed for Telegram Desktop (test DCs 10001+ '
|
||||
'are detected automatically); use for clients that '
|
||||
'signal test DCs as plain 1-3')
|
||||
ap.add_argument('--proxy-protocol', action='store_true',
|
||||
help='Accept PROXY protocol v1 header '
|
||||
'(for use behind nginx/haproxy with proxy_protocol on)')
|
||||
@@ -742,6 +754,7 @@ def main():
|
||||
proxy_config.cfproxy_worker_domains = coerce_domain_list(args.cfproxy_worker_domain)
|
||||
proxy_config.fake_tls_domain = args.fake_tls_domain.strip()
|
||||
proxy_config.proxy_protocol = args.proxy_protocol
|
||||
proxy_config.force_test_dc = args.force_test_dc
|
||||
|
||||
log_level = logging.DEBUG if args.verbose else logging.INFO
|
||||
log_fmt = logging.Formatter('%(asctime)s %(levelname)-5s %(message)s',
|
||||
|
||||
@@ -43,6 +43,15 @@ DC_DEFAULT_IPS: Dict[int, str] = {
|
||||
203: '91.105.192.100'
|
||||
}
|
||||
|
||||
DC_TEST_IPS: Dict[int, str] = {
|
||||
1: '149.154.175.10',
|
||||
2: '149.154.167.40',
|
||||
3: '149.154.175.117',
|
||||
}
|
||||
|
||||
WS_PATH = '/apiws'
|
||||
WS_PATH_TEST = WS_PATH + '_test'
|
||||
|
||||
|
||||
def ws_domains(dc: int, is_media) -> List[str]:
|
||||
if dc == 203:
|
||||
|
||||
Reference in New Issue
Block a user