From 34f743858c99ebf0220c6673d216d8a7fca928c6 Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Tue, 10 Feb 2026 16:49:20 +0500 Subject: [PATCH 1/6] fix small bugs --- mtprotoproxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index fb686ab..3d507f5 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -657,7 +657,7 @@ async def read(self, n): needed_till_full_block = -len(data) % self.block_size if needed_till_full_block > 0: - data += self.upstream.readexactly(needed_till_full_block) + data += await self.upstream.readexactly(needed_till_full_block) return self.decryptor.decrypt(data) async def readexactly(self, n): @@ -1364,7 +1364,7 @@ async def do_direct_handshake(proto_tag, dc_idx, dec_key_and_iv=None): print_err("Got connection refused while trying to connect to", dc, TG_DATACENTER_PORT) return False except ConnectionAbortedError as E: - print_err("The Telegram server connection is bad: %d (%s %s) %s" % (dc_idx, addr, port, E)) + print_err("The Telegram server connection is bad: %d (%s %s) %s" % (dc_idx, dc, TG_DATACENTER_PORT, E)) return False except (OSError, asyncio.TimeoutError) as E: print_err("Unable to connect to", dc, TG_DATACENTER_PORT) From 8920faf65049a92686b9b29edb435340c822452a Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Tue, 17 Feb 2026 21:14:42 +0500 Subject: [PATCH 2/6] better handling for server-side socket closing in connection pool --- mtprotoproxy.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 3d507f5..80d81a4 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -483,7 +483,7 @@ def getrandbytes(self, n): class TgConnectionPool: - MAX_CONNS_IN_POOL = 64 + MAX_CONNS_IN_POOL = 16 def __init__(self): self.pools = {} @@ -500,6 +500,16 @@ async def open_tg_connection(self, host, port, init_func=None): timeout=config.TG_CONNECT_TIMEOUT) return reader_tgt, writer_tgt + def is_conn_dead(self, reader, writer): + if writer.transport.is_closing(): + return True + raw_reader = reader + while hasattr(raw_reader, 'upstream'): + raw_reader = raw_reader.upstream + if raw_reader.at_eof(): + return True + return False + def register_host_port(self, host, port, init_func): if (host, port, init_func) not in self.pools: self.pools[(host, port, init_func)] = [] @@ -519,8 +529,9 @@ async def get_connection(self, host, port, init_func=None): continue reader, writer, *other = task.result() - if writer.transport.is_closing(): + if self.is_conn_dead(reader, writer): self.pools[(host, port, init_func)].remove(task) + writer.transport.abort() continue if not ret: From 1f7ce9e977afb042d30c03d481d0e4bef62454fd Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Wed, 18 Feb 2026 00:04:07 +0500 Subject: [PATCH 3/6] fix typos --- mtprotoproxy.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 80d81a4..dfd54aa 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -139,10 +139,10 @@ def init_config(): # use middle proxy, necessary to show ad conf_dict.setdefault("USE_MIDDLE_PROXY", len(conf_dict["AD_TAG"]) == 16) - # if IPv6 avaliable, use it by default + # if IPv6 available, use it by default conf_dict.setdefault("PREFER_IPV6", socket.has_ipv6) - # disables tg->client trafic reencryption, faster but less secure + # disables tg->client traffic reencryption, faster but less secure conf_dict.setdefault("FAST_MODE", True) # enables some working modes @@ -522,7 +522,7 @@ async def get_connection(self, host, port, init_func=None): self.register_host_port(host, port, init_func) ret = None - for task in self.pools[(host, port, init_func)][::]: + for task in self.pools[(host, port, init_func)][:]: if task.done(): if task.exception(): self.pools[(host, port, init_func)].remove(task) @@ -789,7 +789,7 @@ class MTProtoCompactFrameStreamWriter(LayeredStreamWriterBase): def write(self, data, extra={}): SMALL_PKT_BORDER = 0x7f - LARGE_PKT_BORGER = 256 ** 3 + LARGE_PKT_BORDER = 256 ** 3 if len(data) % 4 != 0: print_err("BUG: MTProtoFrameStreamWriter attempted to send msg with len", len(data)) @@ -802,7 +802,7 @@ def write(self, data, extra={}): if len_div_four < SMALL_PKT_BORDER: return self.upstream.write(bytes([len_div_four]) + data) - elif len_div_four < LARGE_PKT_BORGER: + elif len_div_four < LARGE_PKT_BORDER: return self.upstream.write(b'\x7f' + int.to_bytes(len_div_four, 3, 'little') + data) else: print_err("Attempted to send too large pkt len =", len(data)) @@ -2011,7 +2011,7 @@ async def get_srv_time(): continue line = line[len("Date: "):].decode() srv_time = datetime.datetime.strptime(line, "%a, %d %b %Y %H:%M:%S %Z") - now_time = datetime.datetime.utcnow() + now_time = datetime.datetime.now(datetime.timezone.utc) is_time_skewed = (now_time-srv_time).total_seconds() > MAX_TIME_SKEW if is_time_skewed and config.USE_MIDDLE_PROXY and not disable_middle_proxy: print_err("Time skew detected, please set the clock") @@ -2157,15 +2157,15 @@ def print_tg_info(): for ip in ip_addrs: if config.MODES["classic"]: params = {"server": ip, "port": config.PORT, "secret": secret} - params_encodeded = urllib.parse.urlencode(params, safe=':') - classic_link = "tg://proxy?{}".format(params_encodeded) + params_encoded = urllib.parse.urlencode(params, safe=':') + classic_link = "tg://proxy?{}".format(params_encoded) proxy_links.append({"user": user, "link": classic_link}) print("{}: {}".format(user, classic_link), flush=True) if config.MODES["secure"]: params = {"server": ip, "port": config.PORT, "secret": "dd" + secret} - params_encodeded = urllib.parse.urlencode(params, safe=':') - dd_link = "tg://proxy?{}".format(params_encodeded) + params_encoded = urllib.parse.urlencode(params, safe=':') + dd_link = "tg://proxy?{}".format(params_encoded) proxy_links.append({"user": user, "link": dd_link}) print("{}: {}".format(user, dd_link), flush=True) @@ -2175,8 +2175,8 @@ def print_tg_info(): # tls_secret = bytes.fromhex("ee" + secret) + config.TLS_DOMAIN.encode() # tls_secret_base64 = base64.urlsafe_b64encode(tls_secret) params = {"server": ip, "port": config.PORT, "secret": tls_secret} - params_encodeded = urllib.parse.urlencode(params, safe=':') - tls_link = "tg://proxy?{}".format(params_encodeded) + params_encoded = urllib.parse.urlencode(params, safe=':') + tls_link = "tg://proxy?{}".format(params_encoded) proxy_links.append({"user": user, "link": tls_link}) print("{}: {}".format(user, tls_link), flush=True) From 949ee12ed2e82afbd6e94f1ae1f2743cdd968e06 Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Wed, 18 Feb 2026 01:30:46 +0500 Subject: [PATCH 4/6] use utc timezone when getting the server time --- mtprotoproxy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index dfd54aa..2565b72 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -2011,6 +2011,7 @@ async def get_srv_time(): continue line = line[len("Date: "):].decode() srv_time = datetime.datetime.strptime(line, "%a, %d %b %Y %H:%M:%S %Z") + srv_time = srv_time.replace(tzinfo=datetime.timezone.utc) now_time = datetime.datetime.now(datetime.timezone.utc) is_time_skewed = (now_time-srv_time).total_seconds() > MAX_TIME_SKEW if is_time_skewed and config.USE_MIDDLE_PROXY and not disable_middle_proxy: From 368669a546ddf255c117238134b0abcf99c9e3f4 Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Wed, 18 Feb 2026 02:10:55 +0500 Subject: [PATCH 5/6] don't prefer ipv6 if using middle proxies, they are unstable --- mtprotoproxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 2565b72..9fb58bf 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -139,8 +139,8 @@ def init_config(): # use middle proxy, necessary to show ad conf_dict.setdefault("USE_MIDDLE_PROXY", len(conf_dict["AD_TAG"]) == 16) - # if IPv6 available, use it by default - conf_dict.setdefault("PREFER_IPV6", socket.has_ipv6) + # if IPv6 available, use it by default, IPv6 with middle proxies is unstable now + conf_dict.setdefault("PREFER_IPV6", socket.has_ipv6 and not conf_dict["USE_MIDDLE_PROXY"]) # disables tg->client traffic reencryption, faster but less secure conf_dict.setdefault("FAST_MODE", True) From 0614c35020943b2080c9bd27b5e6336270af389f Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Wed, 18 Feb 2026 02:37:36 +0500 Subject: [PATCH 6/6] implement read timeout, if server sends nothing for 1 min, connection drops --- mtprotoproxy.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 9fb58bf..10de4aa 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -264,6 +264,9 @@ def init_config(): # telegram servers connect timeout in seconds conf_dict.setdefault("TG_CONNECT_TIMEOUT", 10) + # drop connection if no data from telegram server for this many seconds + conf_dict.setdefault("TG_READ_TIMEOUT", 60) + # listen address for IPv4 conf_dict.setdefault("LISTEN_ADDR_IPV4", "0.0.0.0") @@ -1584,7 +1587,11 @@ async def do_middleproxy_handshake(proto_tag, dc_idx, cl_ip, cl_port): async def tg_connect_reader_to_writer(rd, wr, user, rd_buf_size, is_upstream): try: while True: - data = await rd.read(rd_buf_size) + if not is_upstream: + data = await asyncio.wait_for(rd.read(rd_buf_size), + timeout=config.TG_READ_TIMEOUT) + else: + data = await rd.read(rd_buf_size) if isinstance(data, tuple): data, extra = data else: @@ -1605,7 +1612,7 @@ async def tg_connect_reader_to_writer(rd, wr, user, rd_buf_size, is_upstream): wr.write(data, extra) await wr.drain() - except (OSError, asyncio.IncompleteReadError) as e: + except (OSError, asyncio.IncompleteReadError, asyncio.TimeoutError) as e: # print_err(e) pass