mirror of
https://github.com/Flowseal/tg-ws-proxy.git
synced 2026-09-20 09:18:25 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70b982da2c | |||
| 5226a409d4 | |||
| e519c4df4c | |||
| a0605c7447 | |||
| 222cabeee0 | |||
| c2da669576 | |||
| 2c9a0b8a90 | |||
| 87977962dc | |||
| 5a61d8f782 |
@@ -51,6 +51,7 @@ tg-ws-proxy [--port PORT] [--host HOST] [--dc-ip DC:IP ...] [-v]
|
|||||||
| `--no-cfproxy` | `false` | Disable [Cloudflare proxying](./CfProxy.md) attempts |
|
| `--no-cfproxy` | `false` | Disable [Cloudflare proxying](./CfProxy.md) attempts |
|
||||||
| `--cfproxy-domain` | | Specify your own domain for Cloudflare proxying [Learn more](./CfProxy.md). Can be specified multiple times. |
|
| `--cfproxy-domain` | | Specify your own domain for Cloudflare proxying [Learn more](./CfProxy.md). Can be specified multiple times. |
|
||||||
| `--cfproxy-worker-domain` | | Cloudflare Worker domain [Learn more](./CfWorker.md). Can be specified multiple times. |
|
| `--cfproxy-worker-domain` | | Cloudflare Worker domain [Learn more](./CfWorker.md). Can be specified multiple times. |
|
||||||
|
| `--no-secure` | `false` | Use 80 port for CF-proxy and CF-worker connections |
|
||||||
| `--fake-tls-domain` | | Enable Fake TLS masquerading (ee-secret) with specified SNI domain |
|
| `--fake-tls-domain` | | Enable Fake TLS masquerading (ee-secret) with specified SNI domain |
|
||||||
| `--proxy-protocol` | disabled | Accept HAProxy PROXY protocol v1 (for use behind nginx/haproxy with `proxy_protocol on`) |
|
| `--proxy-protocol` | disabled | Accept HAProxy PROXY protocol v1 (for use behind nginx/haproxy with `proxy_protocol on`) |
|
||||||
| `--buf-kb` | `256` | Buffer size in KB |
|
| `--buf-kb` | `256` | Buffer size in KB |
|
||||||
@@ -64,13 +65,13 @@ tg-ws-proxy [--port PORT] [--host HOST] [--dc-ip DC:IP ...] [-v]
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Standard startup
|
# Standard startup
|
||||||
tg-ws-proxy
|
tg-ws-proxy --secret 00112233445566778899aabbccddeeff
|
||||||
|
|
||||||
# Different port and additional DCs
|
# Different port and additional DCs
|
||||||
tg-ws-proxy --port 9050 --dc-ip 1:149.154.175.205 --dc-ip 2:149.154.167.220
|
tg-ws-proxy --port 9050 --dc-ip 1:149.154.175.205 --dc-ip 2:149.154.167.220
|
||||||
|
|
||||||
# With verbose logging
|
# With verbose logging, without direct connections to DCs
|
||||||
tg-ws-proxy -v
|
tg-ws-proxy -v --dc-ip
|
||||||
|
|
||||||
# Fake TLS masquerading (ee-secret)
|
# Fake TLS masquerading (ee-secret)
|
||||||
tg-ws-proxy --fake-tls-domain example.com
|
tg-ws-proxy --fake-tls-domain example.com
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ tg-ws-proxy [--port PORT] [--host HOST] [--dc-ip DC:IP ...] [-v]
|
|||||||
| `--no-cfproxy` | `false` | Отключить попытку [проксирования через Cloudflare](./CfProxy.md) |
|
| `--no-cfproxy` | `false` | Отключить попытку [проксирования через Cloudflare](./CfProxy.md) |
|
||||||
| `--cfproxy-domain` | | Указать свой домен для проксирования через Cloudflare [Подробнее](./CfProxy.md). Можно указать несколько через повторение аргумента. |
|
| `--cfproxy-domain` | | Указать свой домен для проксирования через Cloudflare [Подробнее](./CfProxy.md). Можно указать несколько через повторение аргумента. |
|
||||||
| `--cfproxy-worker-domain` | | Домен Cloudflare Worker [Подробнее](./CfWorker.md). Можно указать несколько через повторение аргумента. |
|
| `--cfproxy-worker-domain` | | Домен Cloudflare Worker [Подробнее](./CfWorker.md). Можно указать несколько через повторение аргумента. |
|
||||||
|
| `--no-secure` | `false` | Принудительно использует 80 порт для CF-proxy и CF-worker |
|
||||||
| `--fake-tls-domain` | | Включить маскировку Fake TLS (ee-secret) с указанным SNI-доменом |
|
| `--fake-tls-domain` | | Включить маскировку Fake TLS (ee-secret) с указанным SNI-доменом |
|
||||||
| `--proxy-protocol` | выкл. | Принимать HAProxy PROXY protocol v1 (для работы за nginx/haproxy с `proxy_protocol on`) |
|
| `--proxy-protocol` | выкл. | Принимать HAProxy PROXY protocol v1 (для работы за nginx/haproxy с `proxy_protocol on`) |
|
||||||
| `--buf-kb` | `256` | Размер буфера в КБ |
|
| `--buf-kb` | `256` | Размер буфера в КБ |
|
||||||
@@ -64,13 +65,13 @@ tg-ws-proxy [--port PORT] [--host HOST] [--dc-ip DC:IP ...] [-v]
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Стандартный запуск
|
# Стандартный запуск
|
||||||
tg-ws-proxy
|
tg-ws-proxy --secret 00112233445566778899aabbccddeeff
|
||||||
|
|
||||||
# Другой порт и дополнительные DC
|
# Другой порт и дополнительные DC
|
||||||
tg-ws-proxy --port 9050 --dc-ip 1:149.154.175.205 --dc-ip 2:149.154.167.220
|
tg-ws-proxy --port 9050 --dc-ip 1:149.154.175.205 --dc-ip 2:149.154.167.220
|
||||||
|
|
||||||
# С подробным логированием
|
# С подробным логированием, без прямого подключения к DC
|
||||||
tg-ws-proxy -v
|
tg-ws-proxy -v --dc-ip
|
||||||
|
|
||||||
# Fake TLS маскировка (ee-secret)
|
# Fake TLS маскировка (ee-secret)
|
||||||
tg-ws-proxy --fake-tls-domain example.com
|
tg-ws-proxy --fake-tls-domain example.com
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
|
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
|
||||||
VSVersionInfo(
|
VSVersionInfo(
|
||||||
ffi=FixedFileInfo(
|
ffi=FixedFileInfo(
|
||||||
filevers=(1, 10, 0, 0),
|
filevers=(1, 10, 4, 0),
|
||||||
prodvers=(1, 10, 0, 0),
|
prodvers=(1, 10, 4, 0),
|
||||||
mask=0x3f,
|
mask=0x3f,
|
||||||
flags=0x0,
|
flags=0x0,
|
||||||
OS=0x40004,
|
OS=0x40004,
|
||||||
@@ -21,12 +21,12 @@ VSVersionInfo(
|
|||||||
[
|
[
|
||||||
StringStruct(u'CompanyName', u'Flowseal'),
|
StringStruct(u'CompanyName', u'Flowseal'),
|
||||||
StringStruct(u'FileDescription', u'Telegram Desktop WebSocket Bridge Proxy'),
|
StringStruct(u'FileDescription', u'Telegram Desktop WebSocket Bridge Proxy'),
|
||||||
StringStruct(u'FileVersion', u'1.10.0.0'),
|
StringStruct(u'FileVersion', u'1.10.4.0'),
|
||||||
StringStruct(u'InternalName', u'TgWsProxy'),
|
StringStruct(u'InternalName', u'TgWsProxy'),
|
||||||
StringStruct(u'LegalCopyright', u'Copyright (c) Flowseal. MIT License.'),
|
StringStruct(u'LegalCopyright', u'Copyright (c) Flowseal. MIT License.'),
|
||||||
StringStruct(u'OriginalFilename', u'TgWsProxy.exe'),
|
StringStruct(u'OriginalFilename', u'TgWsProxy.exe'),
|
||||||
StringStruct(u'ProductName', u'TG WS Proxy'),
|
StringStruct(u'ProductName', u'TG WS Proxy'),
|
||||||
StringStruct(u'ProductVersion', u'1.10.0.0'),
|
StringStruct(u'ProductVersion', u'1.10.4.0'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
from .config import parse_dc_ip_list, proxy_config, coerce_domain_list
|
from .config import parse_dc_ip_list, proxy_config, coerce_domain_list
|
||||||
from .utils import get_link_host, build_github_opener
|
from .utils import get_link_host, build_github_opener
|
||||||
|
|
||||||
__version__ = "1.10.2"
|
__version__ = "1.10.4"
|
||||||
|
|
||||||
__all__ = ["__version__", "get_link_host", "proxy_config", "parse_dc_ip_list", "build_github_opener", "coerce_domain_list"]
|
__all__ = ["__version__", "get_link_host", "proxy_config", "parse_dc_ip_list", "build_github_opener", "coerce_domain_list"]
|
||||||
+4
-2
@@ -201,7 +201,8 @@ async def _cfproxy_worker_fallback(reader, writer, relay_init, label,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
ws = await RawWebSocket.connect(worker_domain, worker_domain,
|
ws = await RawWebSocket.connect(worker_domain, worker_domain,
|
||||||
timeout=10.0, path=path)
|
timeout=10.0, path=path,
|
||||||
|
secure=not proxy_config.disable_secure)
|
||||||
break
|
break
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
cf_worker_pool.report_failure(worker_domain, exc)
|
cf_worker_pool.report_failure(worker_domain, exc)
|
||||||
@@ -234,7 +235,8 @@ async def _cfproxy_fallback(reader, writer, relay_init, label,
|
|||||||
for base_domain in balancer.get_domains_for_dc(dc):
|
for base_domain in balancer.get_domains_for_dc(dc):
|
||||||
domain = f'kws{dc}.{base_domain}'
|
domain = f'kws{dc}.{base_domain}'
|
||||||
try:
|
try:
|
||||||
ws = await RawWebSocket.connect(domain, domain, timeout=10.0)
|
ws = await RawWebSocket.connect(domain, domain, timeout=10.0,
|
||||||
|
secure=not proxy_config.disable_secure)
|
||||||
chosen_domain = base_domain
|
chosen_domain = base_domain
|
||||||
break
|
break
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class ProxyConfig:
|
|||||||
fallback_cfproxy: bool = True
|
fallback_cfproxy: bool = True
|
||||||
cfproxy_user_domains: List[str] = field(default_factory=list)
|
cfproxy_user_domains: List[str] = field(default_factory=list)
|
||||||
cfproxy_worker_domains: List[str] = field(default_factory=list)
|
cfproxy_worker_domains: List[str] = field(default_factory=list)
|
||||||
|
disable_secure: bool = False
|
||||||
fake_tls_domain: str = ''
|
fake_tls_domain: str = ''
|
||||||
proxy_protocol: bool = False
|
proxy_protocol: bool = False
|
||||||
force_test_dc: bool = False
|
force_test_dc: bool = False
|
||||||
|
|||||||
+2
-1
@@ -289,7 +289,8 @@ class _CfWorkerPool:
|
|||||||
for worker_domain in self.available_domains(worker_domains):
|
for worker_domain in self.available_domains(worker_domains):
|
||||||
try:
|
try:
|
||||||
ws = await RawWebSocket.connect(
|
ws = await RawWebSocket.connect(
|
||||||
worker_domain, worker_domain, timeout=8, path=path)
|
worker_domain, worker_domain, timeout=8, path=path,
|
||||||
|
secure=not proxy_config.disable_secure)
|
||||||
return ws, worker_domain
|
return ws, worker_domain
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.report_failure(worker_domain, exc)
|
self.report_failure(worker_domain, exc)
|
||||||
|
|||||||
+19
-9
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import ssl
|
import ssl
|
||||||
|
import certifi
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
import struct
|
import struct
|
||||||
@@ -21,10 +22,9 @@ _st_BBQ4s = struct.Struct('>BBQ4s')
|
|||||||
_st_H = struct.Struct('>H')
|
_st_H = struct.Struct('>H')
|
||||||
_st_Q = struct.Struct('>Q')
|
_st_Q = struct.Struct('>Q')
|
||||||
|
|
||||||
_ssl_ctx = ssl.create_default_context()
|
_ssl_ctx = ssl.create_default_context(cafile=certifi.where())
|
||||||
_ssl_ctx.check_hostname = False
|
_ssl_ctx_fronting = ssl.create_default_context(cafile=certifi.where())
|
||||||
_ssl_ctx.verify_mode = ssl.CERT_NONE
|
_ssl_ctx_fronting.check_hostname = False
|
||||||
|
|
||||||
|
|
||||||
class WsHandshakeError(Exception):
|
class WsHandshakeError(Exception):
|
||||||
def __init__(self, status_code: int, status_line: str,
|
def __init__(self, status_code: int, status_line: str,
|
||||||
@@ -87,14 +87,24 @@ class RawWebSocket:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
async def connect(host: str, domain: str, timeout: float = 10.0,
|
async def connect(host: str, domain: str, timeout: float = 10.0,
|
||||||
path: str = '/apiws', *,
|
path: str = '/apiws', *,
|
||||||
sni: Optional[str] = None) -> 'RawWebSocket':
|
sni: Optional[str] = None, secure = True) -> 'RawWebSocket':
|
||||||
|
ssl = _ssl_ctx_fronting if sni else _ssl_ctx
|
||||||
|
|
||||||
if sni is None:
|
if sni is None:
|
||||||
sni = domain
|
sni = domain
|
||||||
|
|
||||||
reader, writer = await asyncio.wait_for(
|
reader, writer = await asyncio.wait_for(
|
||||||
asyncio.open_connection(host, 443, ssl=_ssl_ctx,
|
(
|
||||||
server_hostname=sni),
|
asyncio.open_connection(
|
||||||
timeout=min(timeout, 10))
|
host, 443,
|
||||||
|
ssl=ssl,
|
||||||
|
server_hostname=sni,
|
||||||
|
)
|
||||||
|
if secure
|
||||||
|
else asyncio.open_connection(host, 80)
|
||||||
|
),
|
||||||
|
timeout=min(timeout, 10),
|
||||||
|
)
|
||||||
|
|
||||||
set_sock_opts(writer.transport, proxy_config.buffer_size)
|
set_sock_opts(writer.transport, proxy_config.buffer_size)
|
||||||
|
|
||||||
@@ -281,4 +291,4 @@ class RawWebSocket:
|
|||||||
payload = await self.reader.readexactly(length)
|
payload = await self.reader.readexactly(length)
|
||||||
return opcode, _xor_mask(payload, mask_key), fin
|
return opcode, _xor_mask(payload, mask_key), fin
|
||||||
payload = await self.reader.readexactly(length)
|
payload = await self.reader.readexactly(length)
|
||||||
return opcode, payload, fin
|
return opcode, payload, fin
|
||||||
|
|||||||
+13
-3
@@ -531,6 +531,8 @@ async def _run(stop_event: Optional[asyncio.Event] = None):
|
|||||||
if proxy_config.cfproxy_worker_domains:
|
if proxy_config.cfproxy_worker_domains:
|
||||||
log.info(" CF worker: enabled (%s)",
|
log.info(" CF worker: enabled (%s)",
|
||||||
", ".join(proxy_config.cfproxy_worker_domains))
|
", ".join(proxy_config.cfproxy_worker_domains))
|
||||||
|
if proxy_config.disable_secure:
|
||||||
|
log.info(" No secure: enabled (port 80 for CF proxy/worker)")
|
||||||
log.info("=" * 60)
|
log.info("=" * 60)
|
||||||
log.info(" Connect:")
|
log.info(" Connect:")
|
||||||
if ftls:
|
if ftls:
|
||||||
@@ -647,7 +649,7 @@ def main():
|
|||||||
ap.add_argument('--secret', type=str, default=None,
|
ap.add_argument('--secret', type=str, default=None,
|
||||||
help='MTProto proxy secret (32 hex chars). '
|
help='MTProto proxy secret (32 hex chars). '
|
||||||
'Auto-generated if not provided.')
|
'Auto-generated if not provided.')
|
||||||
ap.add_argument('--dc-ip', metavar='DC:IP', action='append',
|
ap.add_argument('--dc-ip', metavar='DC:IP', nargs='?', action='append', const=None,
|
||||||
help='Target IP for a DC, e.g. --dc-ip 2:149.154.167.220')
|
help='Target IP for a DC, e.g. --dc-ip 2:149.154.167.220')
|
||||||
ap.add_argument('-v', '--verbose', action='store_true',
|
ap.add_argument('-v', '--verbose', action='store_true',
|
||||||
help='Debug logging')
|
help='Debug logging')
|
||||||
@@ -673,6 +675,8 @@ def main():
|
|||||||
'repeatable for multiple domains)')
|
'repeatable for multiple domains)')
|
||||||
ap.add_argument('--no-cfproxy', action='store_true',
|
ap.add_argument('--no-cfproxy', action='store_true',
|
||||||
help='Disable Cloudflare proxy fallback')
|
help='Disable Cloudflare proxy fallback')
|
||||||
|
ap.add_argument('--no-secure', action='store_true',
|
||||||
|
help='Use 80 port for CF-proxy and CF-worker connections')
|
||||||
ap.add_argument('--fake-tls-domain', type=str, default='',
|
ap.add_argument('--fake-tls-domain', type=str, default='',
|
||||||
metavar='DOMAIN',
|
metavar='DOMAIN',
|
||||||
help='Enable Fake TLS (ee-secret) masking with the given '
|
help='Enable Fake TLS (ee-secret) masking with the given '
|
||||||
@@ -687,8 +691,13 @@ def main():
|
|||||||
'(for use behind nginx/haproxy with proxy_protocol on)')
|
'(for use behind nginx/haproxy with proxy_protocol on)')
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
if not args.dc_ip:
|
if args.dc_ip is None:
|
||||||
args.dc_ip = ['2:149.154.167.220', '4:149.154.167.220']
|
args.dc_ip = [
|
||||||
|
'2:149.154.167.220',
|
||||||
|
'4:149.154.167.220',
|
||||||
|
]
|
||||||
|
elif None in args.dc_ip:
|
||||||
|
args.dc_ip = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dc_redirects = parse_dc_ip_list(args.dc_ip)
|
dc_redirects = parse_dc_ip_list(args.dc_ip)
|
||||||
@@ -719,6 +728,7 @@ def main():
|
|||||||
proxy_config.fallback_cfproxy = not args.no_cfproxy
|
proxy_config.fallback_cfproxy = not args.no_cfproxy
|
||||||
proxy_config.cfproxy_user_domains = coerce_domain_list(args.cfproxy_domain)
|
proxy_config.cfproxy_user_domains = coerce_domain_list(args.cfproxy_domain)
|
||||||
proxy_config.cfproxy_worker_domains = coerce_domain_list(args.cfproxy_worker_domain)
|
proxy_config.cfproxy_worker_domains = coerce_domain_list(args.cfproxy_worker_domain)
|
||||||
|
proxy_config.disable_secure = args.no_secure
|
||||||
proxy_config.fake_tls_domain = args.fake_tls_domain.strip()
|
proxy_config.fake_tls_domain = args.fake_tls_domain.strip()
|
||||||
proxy_config.proxy_protocol = args.proxy_protocol
|
proxy_config.proxy_protocol = args.proxy_protocol
|
||||||
proxy_config.force_test_dc = args.force_test_dc
|
proxy_config.force_test_dc = args.force_test_dc
|
||||||
|
|||||||
+33
-20
@@ -44,19 +44,22 @@ _CFWORKER_TEST_DST = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _run_connectivity_test(cases: list) -> dict:
|
def _run_connectivity_test(cases: list, *, secure: bool = True) -> dict:
|
||||||
import base64
|
import base64
|
||||||
|
from contextlib import nullcontext
|
||||||
import ssl
|
import ssl
|
||||||
|
import certifi
|
||||||
import socket as _socket
|
import socket as _socket
|
||||||
|
|
||||||
ctx = ssl.create_default_context()
|
ctx = ssl.create_default_context(cafile=certifi.where()) if secure else None
|
||||||
ctx.check_hostname = False
|
port = 443 if secure else 80
|
||||||
ctx.verify_mode = ssl.CERT_NONE
|
|
||||||
results = {}
|
results = {}
|
||||||
for dc, connect_host, sni_host, req_host, path in cases:
|
for dc, connect_host, sni_host, req_host, path in cases:
|
||||||
try:
|
try:
|
||||||
with _socket.create_connection((connect_host, 443), timeout=5) as raw:
|
with _socket.create_connection((connect_host, port), timeout=5) as raw:
|
||||||
with ctx.wrap_socket(raw, server_hostname=sni_host) as ssock:
|
connection = (ctx.wrap_socket(raw, server_hostname=sni_host)
|
||||||
|
if secure else nullcontext(raw))
|
||||||
|
with connection as ssock:
|
||||||
ws_key = base64.b64encode(os.urandom(16)).decode()
|
ws_key = base64.b64encode(os.urandom(16)).decode()
|
||||||
req = (
|
req = (
|
||||||
f"GET {path} HTTP/1.1\r\n"
|
f"GET {path} HTTP/1.1\r\n"
|
||||||
@@ -91,36 +94,36 @@ def _run_connectivity_test(cases: list) -> dict:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
def _run_cfproxy_connectivity_test(domain: str) -> dict:
|
def _run_cfproxy_connectivity_test(domain: str, *, secure: bool = True) -> dict:
|
||||||
cases = []
|
cases = []
|
||||||
for dc in _CFPROXY_TEST_DCS:
|
for dc in _CFPROXY_TEST_DCS:
|
||||||
host = f"kws{dc}.{domain}"
|
host = f"kws{dc}.{domain}"
|
||||||
cases.append((dc, host, host, host, "/apiws"))
|
cases.append((dc, host, host, host, "/apiws"))
|
||||||
return _run_connectivity_test(cases)
|
return _run_connectivity_test(cases, secure=secure)
|
||||||
|
|
||||||
|
|
||||||
def _run_cfworker_connectivity_test(domain: str) -> dict:
|
def _run_cfworker_connectivity_test(domain: str, *, secure: bool = True) -> dict:
|
||||||
cases = []
|
cases = []
|
||||||
for dc in _CFPROXY_TEST_DCS:
|
for dc in _CFPROXY_TEST_DCS:
|
||||||
dst = _CFWORKER_TEST_DST[dc]
|
dst = _CFWORKER_TEST_DST[dc]
|
||||||
path = f"/apiws?dst={dst}&dc={dc}&media=0"
|
path = f"/apiws?dst={dst}&dc={dc}&media=0"
|
||||||
cases.append((dc, domain, domain, domain, path))
|
cases.append((dc, domain, domain, domain, path))
|
||||||
return _run_connectivity_test(cases)
|
return _run_connectivity_test(cases, secure=secure)
|
||||||
|
|
||||||
|
|
||||||
def _run_cfproxy_multi_test(domains: list) -> dict:
|
def _run_cfproxy_multi_test(domains: list, *, secure: bool = True) -> dict:
|
||||||
return {domain: _run_cfproxy_connectivity_test(domain) for domain in domains}
|
return {domain: _run_cfproxy_connectivity_test(domain, secure=secure) for domain in domains}
|
||||||
|
|
||||||
|
|
||||||
def _run_cfworker_multi_test(domains: list) -> dict:
|
def _run_cfworker_multi_test(domains: list, *, secure: bool = True) -> dict:
|
||||||
return {domain: _run_cfworker_connectivity_test(domain) for domain in domains}
|
return {domain: _run_cfworker_connectivity_test(domain, secure=secure) for domain in domains}
|
||||||
|
|
||||||
|
|
||||||
def _run_cfproxy_auto_test(domains: list) -> tuple:
|
def _run_cfproxy_auto_test(domains: list, *, secure: bool = True) -> tuple:
|
||||||
merged: dict = {}
|
merged: dict = {}
|
||||||
best_domain = None
|
best_domain = None
|
||||||
for domain in reversed(domains):
|
for domain in reversed(domains):
|
||||||
res = _run_cfproxy_connectivity_test(domain)
|
res = _run_cfproxy_connectivity_test(domain, secure=secure)
|
||||||
if all(v is True for v in res.values()):
|
if all(v is True for v in res.values()):
|
||||||
return domain, res
|
return domain, res
|
||||||
for dc, v in res.items():
|
for dc, v in res.items():
|
||||||
@@ -350,6 +353,7 @@ class TrayConfigFormWidgets:
|
|||||||
secret_var: Any
|
secret_var: Any
|
||||||
dc_textbox: Any
|
dc_textbox: Any
|
||||||
verbose_var: Any
|
verbose_var: Any
|
||||||
|
no_secure_var: Any
|
||||||
adv_entries: List[Any]
|
adv_entries: List[Any]
|
||||||
adv_keys: Tuple[str, ...]
|
adv_keys: Tuple[str, ...]
|
||||||
autostart_var: Optional[Any]
|
autostart_var: Optional[Any]
|
||||||
@@ -536,6 +540,7 @@ def install_tray_config_form(
|
|||||||
_cf_test_btn = [None]
|
_cf_test_btn = [None]
|
||||||
|
|
||||||
def _on_cf_test():
|
def _on_cf_test():
|
||||||
|
secure = not no_secure_var.get()
|
||||||
user_domains = (
|
user_domains = (
|
||||||
coerce_domain_list(cfproxy_user_domain_var.get())
|
coerce_domain_list(cfproxy_user_domain_var.get())
|
||||||
if cf_custom_cb_var.get() else []
|
if cf_custom_cb_var.get() else []
|
||||||
@@ -547,7 +552,7 @@ def install_tray_config_form(
|
|||||||
if user_domains:
|
if user_domains:
|
||||||
def _worker():
|
def _worker():
|
||||||
try:
|
try:
|
||||||
per = _run_cfproxy_multi_test(user_domains)
|
per = _run_cfproxy_multi_test(user_domains, secure=secure)
|
||||||
if btn:
|
if btn:
|
||||||
btn.after(
|
btn.after(
|
||||||
0,
|
0,
|
||||||
@@ -564,7 +569,7 @@ def install_tray_config_form(
|
|||||||
else:
|
else:
|
||||||
def _worker_auto():
|
def _worker_auto():
|
||||||
try:
|
try:
|
||||||
ok_domain, res = _run_cfproxy_auto_test(balancer.domains)
|
ok_domain, res = _run_cfproxy_auto_test(balancer.domains, secure=secure)
|
||||||
if btn:
|
if btn:
|
||||||
btn.after(
|
btn.after(
|
||||||
0,
|
0,
|
||||||
@@ -672,6 +677,7 @@ def install_tray_config_form(
|
|||||||
btn.configure(state="normal" if enabled else "disabled")
|
btn.configure(state="normal" if enabled else "disabled")
|
||||||
|
|
||||||
def _on_cfworker_test():
|
def _on_cfworker_test():
|
||||||
|
secure = not no_secure_var.get()
|
||||||
domains = coerce_domain_list(cfproxy_worker_domain_var.get())
|
domains = coerce_domain_list(cfproxy_worker_domain_var.get())
|
||||||
btn = _cfworker_test_btn[0]
|
btn = _cfworker_test_btn[0]
|
||||||
if not cfproxy_worker_enabled_var.get() or not domains or btn is None:
|
if not cfproxy_worker_enabled_var.get() or not domains or btn is None:
|
||||||
@@ -681,7 +687,7 @@ def install_tray_config_form(
|
|||||||
|
|
||||||
def _worker():
|
def _worker():
|
||||||
try:
|
try:
|
||||||
per = _run_cfworker_multi_test(domains)
|
per = _run_cfworker_multi_test(domains, secure=secure)
|
||||||
btn.after(
|
btn.after(
|
||||||
0,
|
0,
|
||||||
lambda: _show_multi_connectivity_results(
|
lambda: _show_multi_connectivity_results(
|
||||||
@@ -730,6 +736,11 @@ def install_tray_config_form(
|
|||||||
verbose_cb.pack(anchor="w", pady=(0, 6))
|
verbose_cb.pack(anchor="w", pady=(0, 6))
|
||||||
attach_ctk_tooltip(verbose_cb, t("tip.verbose"))
|
attach_ctk_tooltip(verbose_cb, t("tip.verbose"))
|
||||||
|
|
||||||
|
no_secure_var = ctk.BooleanVar(value=cfg.get("no_secure", False))
|
||||||
|
no_secure_cb = _checkbox(ctk, log_inner, theme, t("label.no_secure"), no_secure_var)
|
||||||
|
no_secure_cb.pack(anchor="w", pady=(0, 6))
|
||||||
|
attach_ctk_tooltip(no_secure_cb, t("tip.no_secure"))
|
||||||
|
|
||||||
adv_frame = ctk.CTkFrame(log_inner, fg_color="transparent")
|
adv_frame = ctk.CTkFrame(log_inner, fg_color="transparent")
|
||||||
adv_frame.pack(fill="x")
|
adv_frame.pack(fill="x")
|
||||||
|
|
||||||
@@ -823,7 +834,7 @@ def install_tray_config_form(
|
|||||||
|
|
||||||
return TrayConfigFormWidgets(
|
return TrayConfigFormWidgets(
|
||||||
host_var=host_var, port_var=port_var, secret_var=secret_var,
|
host_var=host_var, port_var=port_var, secret_var=secret_var,
|
||||||
dc_textbox=dc_textbox, verbose_var=verbose_var,
|
dc_textbox=dc_textbox, verbose_var=verbose_var, no_secure_var=no_secure_var,
|
||||||
adv_entries=adv_entries, adv_keys=adv_keys,
|
adv_entries=adv_entries, adv_keys=adv_keys,
|
||||||
autostart_var=autostart_var, check_updates_var=check_updates_var,
|
autostart_var=autostart_var, check_updates_var=check_updates_var,
|
||||||
cfproxy_var=cfproxy_var,
|
cfproxy_var=cfproxy_var,
|
||||||
@@ -935,6 +946,8 @@ def validate_config_form(
|
|||||||
new_cfg["appearance"] = _appearance_to_cfg(widgets.appearance_var.get())
|
new_cfg["appearance"] = _appearance_to_cfg(widgets.appearance_var.get())
|
||||||
if widgets.language_var is not None:
|
if widgets.language_var is not None:
|
||||||
new_cfg["language"] = language_from_label(widgets.language_var.get()).value
|
new_cfg["language"] = language_from_label(widgets.language_var.get()).value
|
||||||
|
if widgets.no_secure_var is not None:
|
||||||
|
new_cfg["no_secure"] = bool(widgets.no_secure_var.get())
|
||||||
return new_cfg
|
return new_cfg
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -32,6 +32,7 @@
|
|||||||
"label.cf_custom_domain": "Custom domain",
|
"label.cf_custom_domain": "Custom domain",
|
||||||
"label.cfworker_domains": "Cloudflare Worker domains (comma-separated)",
|
"label.cfworker_domains": "Cloudflare Worker domains (comma-separated)",
|
||||||
"label.verbose": "Verbose logging",
|
"label.verbose": "Verbose logging",
|
||||||
|
"label.no_secure": "Disable TLS for CF-proxy and CF-worker",
|
||||||
"label.buf_kb": "Buffer, KB (default 256)",
|
"label.buf_kb": "Buffer, KB (default 256)",
|
||||||
"label.pool_size": "WebSocket session pool (default 4)",
|
"label.pool_size": "WebSocket session pool (default 4)",
|
||||||
"label.log_max_mb": "Max log size, MB (default 5)",
|
"label.log_max_mb": "Max log size, MB (default 5)",
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
"tip.secret": "Secret key for client authorization",
|
"tip.secret": "Secret key for client authorization",
|
||||||
"tip.dc": "Mapping of Telegram data center (DC) number to web.telegram.org dc server IP.\nEach line: «number:IP», e.g. 4:149.154.167.220. The proxy routes traffic to Telegram servers using these rules\n\nIf connection fails then fallbacks are used",
|
"tip.dc": "Mapping of Telegram data center (DC) number to web.telegram.org dc server IP.\nEach line: «number:IP», e.g. 4:149.154.167.220. The proxy routes traffic to Telegram servers using these rules\n\nIf connection fails then fallbacks are used",
|
||||||
"tip.verbose": "When enabled, more details are written to the log file — useful for troubleshooting",
|
"tip.verbose": "When enabled, more details are written to the log file — useful for troubleshooting",
|
||||||
|
"tip.no_secure": "When enabled, the proxy will use port 80 for CF-proxy and CF-worker connections",
|
||||||
"tip.buf_kb": "Receive/send buffer size in kilobytes.\nA larger value allocates more memory per socket",
|
"tip.buf_kb": "Receive/send buffer size in kilobytes.\nA larger value allocates more memory per socket",
|
||||||
"tip.pool": "How many parallel WebSocket sessions per data center can be kept open.\nIncreasing may help under high load",
|
"tip.pool": "How many parallel WebSocket sessions per data center can be kept open.\nIncreasing may help under high load",
|
||||||
"tip.log_mb": "Maximum log file size; the file is overwritten when the limit is reached",
|
"tip.log_mb": "Maximum log file size; the file is overwritten when the limit is reached",
|
||||||
@@ -118,7 +120,7 @@
|
|||||||
|
|
||||||
"dialog.restart_title": "Restart?",
|
"dialog.restart_title": "Restart?",
|
||||||
"dialog.restart_body": "Settings saved.\n\nRestart the proxy now?",
|
"dialog.restart_body": "Settings saved.\n\nRestart the proxy now?",
|
||||||
"dialog.already_running": "Application is already running.",
|
"dialog.already_running": "Application is already running, check the tray",
|
||||||
"dialog.log_not_found": "Log file has not been created yet.",
|
"dialog.log_not_found": "Log file has not been created yet.",
|
||||||
"dialog.ctk_missing": "customtkinter is not installed.",
|
"dialog.ctk_missing": "customtkinter is not installed.",
|
||||||
"dialog.copy_ok": "Link copied to clipboard, send it in Telegram and click it:\n{url}",
|
"dialog.copy_ok": "Link copied to clipboard, send it in Telegram and click it:\n{url}",
|
||||||
|
|||||||
+3
-1
@@ -32,6 +32,7 @@
|
|||||||
"label.cf_custom_domain": "Свой домен",
|
"label.cf_custom_domain": "Свой домен",
|
||||||
"label.cfworker_domains": "Cloudflare Worker домены (через запятую)",
|
"label.cfworker_domains": "Cloudflare Worker домены (через запятую)",
|
||||||
"label.verbose": "Подробное логирование (verbose)",
|
"label.verbose": "Подробное логирование (verbose)",
|
||||||
|
"label.no_secure": "Выключиь TLS для CF-прокси и CF-worker",
|
||||||
"label.buf_kb": "Буфер, КБ (по умолчанию 256)",
|
"label.buf_kb": "Буфер, КБ (по умолчанию 256)",
|
||||||
"label.pool_size": "Пул WebSocket-сессий (по умолчанию 4)",
|
"label.pool_size": "Пул WebSocket-сессий (по умолчанию 4)",
|
||||||
"label.log_max_mb": "Макс. размер лога, МБ (по умолчанию 5)",
|
"label.log_max_mb": "Макс. размер лога, МБ (по умолчанию 5)",
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
"tip.secret": "Секретный ключ для авторизации клиентов",
|
"tip.secret": "Секретный ключ для авторизации клиентов",
|
||||||
"tip.dc": "Соответствие номера датацентра Telegram (DC) и IP-адреса сервера.\nКаждая строка: «номер:IP», например 4:149.154.167.220. Прокси по этим правилам направляет трафик к нужным серверам Telegram\n\nЕсли у вас не работают медиа и работает CF-прокси, то попробуйте убрать строку 2:149.154.167.220",
|
"tip.dc": "Соответствие номера датацентра Telegram (DC) и IP-адреса сервера.\nКаждая строка: «номер:IP», например 4:149.154.167.220. Прокси по этим правилам направляет трафик к нужным серверам Telegram\n\nЕсли у вас не работают медиа и работает CF-прокси, то попробуйте убрать строку 2:149.154.167.220",
|
||||||
"tip.verbose": "Если включено, в файл логов пишется больше подробностей — необходимо при поиске неполадок",
|
"tip.verbose": "Если включено, в файл логов пишется больше подробностей — необходимо при поиске неполадок",
|
||||||
|
"tip.no_secure": "Если включено, прокси будет использовать 80 порт для CF-прокси и CF-worker подключений",
|
||||||
"tip.buf_kb": "Размер буфера приёма/передачи в килобайтах.\nБольше значение — больше выделение памяти на сокет",
|
"tip.buf_kb": "Размер буфера приёма/передачи в килобайтах.\nБольше значение — больше выделение памяти на сокет",
|
||||||
"tip.pool": "Сколько параллельных WebSocket-сессий к одному датацентру можно держать.\nУвеличение может помочь при высокой нагрузке",
|
"tip.pool": "Сколько параллельных WebSocket-сессий к одному датацентру можно держать.\nУвеличение может помочь при высокой нагрузке",
|
||||||
"tip.log_mb": "Максимальный размер файла лога; при достижении лимита файл перезаписывается",
|
"tip.log_mb": "Максимальный размер файла лога; при достижении лимита файл перезаписывается",
|
||||||
@@ -118,7 +120,7 @@
|
|||||||
|
|
||||||
"dialog.restart_title": "Перезапустить?",
|
"dialog.restart_title": "Перезапустить?",
|
||||||
"dialog.restart_body": "Настройки сохранены.\n\nПерезапустить прокси сейчас?",
|
"dialog.restart_body": "Настройки сохранены.\n\nПерезапустить прокси сейчас?",
|
||||||
"dialog.already_running": "Приложение уже запущено.",
|
"dialog.already_running": "Приложение уже запущено, проверьте трей.",
|
||||||
"dialog.log_not_found": "Файл логов ещё не создан.",
|
"dialog.log_not_found": "Файл логов ещё не создан.",
|
||||||
"dialog.ctk_missing": "customtkinter не установлен.",
|
"dialog.ctk_missing": "customtkinter не установлен.",
|
||||||
"dialog.copy_ok": "Ссылка скопирована в буфер обмена, отправьте её в Telegram и нажмите по ней ЛКМ:\n{url}",
|
"dialog.copy_ok": "Ссылка скопирована в буфер обмена, отправьте её в Telegram и нажмите по ней ЛКМ:\n{url}",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ _TRAY_DEFAULTS_COMMON: Dict[str, Any] = {
|
|||||||
"cfproxy_worker_enabled": False,
|
"cfproxy_worker_enabled": False,
|
||||||
"cfproxy_worker_domain": [],
|
"cfproxy_worker_domain": [],
|
||||||
"force_test_dc": False,
|
"force_test_dc": False,
|
||||||
|
"no_secure": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -374,6 +374,7 @@ def apply_proxy_config(cfg: dict) -> bool:
|
|||||||
else []
|
else []
|
||||||
)
|
)
|
||||||
pc.force_test_dc = cfg.get("force_test_dc", DEFAULT_CONFIG["force_test_dc"])
|
pc.force_test_dc = cfg.get("force_test_dc", DEFAULT_CONFIG["force_test_dc"])
|
||||||
|
pc.disable_secure = cfg.get("no_secure", DEFAULT_CONFIG["no_secure"])
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user