From c2ad0de6651d2f9a9fc11bac0c3a3dcf650dffae Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Sun, 8 Jul 2018 17:48:13 +0500 Subject: [PATCH 1/5] increase default buffer limit --- mtprotoproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 80716b1..e8d2275 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -149,7 +149,7 @@ PREFER_IPV6 = config.get("PREFER_IPV6", socket.has_ipv6) FAST_MODE = config.get("FAST_MODE", True) STATS_PRINT_PERIOD = config.get("STATS_PRINT_PERIOD", 600) PROXY_INFO_UPDATE_PERIOD = config.get("PROXY_INFO_UPDATE_PERIOD", 24*60*60) -TO_CLT_BUFSIZE = config.get("TO_CLT_BUFSIZE", 8192) +TO_CLT_BUFSIZE = config.get("TO_CLT_BUFSIZE", 16384) TO_TG_BUFSIZE = config.get("TO_TG_BUFSIZE", 65536) CLIENT_KEEPALIVE = config.get("CLIENT_KEEPALIVE", 10*60) CLIENT_HANDSHAKE_TIMEOUT = config.get("CLIENT_HANDSHAKE_TIMEOUT", 10) From 647b6f6eddffbc2945fa4e28a189b2761526dd7e Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Sun, 8 Jul 2018 19:05:45 +0500 Subject: [PATCH 2/5] add connect retrying --- mtprotoproxy.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index e8d2275..cbaaec1 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -154,6 +154,7 @@ TO_TG_BUFSIZE = config.get("TO_TG_BUFSIZE", 65536) CLIENT_KEEPALIVE = config.get("CLIENT_KEEPALIVE", 10*60) CLIENT_HANDSHAKE_TIMEOUT = config.get("CLIENT_HANDSHAKE_TIMEOUT", 10) CLIENT_ACK_TIMEOUT = config.get("CLIENT_ACK_TIMEOUT", 5*60) +TG_CONNECT_TIMEOUT = config.get("TG_CONNECT_TIMEOUT", 10) TG_DATACENTER_PORT = 443 @@ -591,6 +592,21 @@ def set_bufsizes(sock, recv_buf, send_buf): sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, send_buf) +async def open_connection_tryer(addr, port, limit, timeout, max_attempts=3): + for attempt in range(max_attempts-1): + try: + task = asyncio.open_connection(addr, port, limit=limit) + reader_tgt, writer_tgt = await asyncio.wait_for(task, timeout=timeout) + return reader_tgt, writer_tgt + except (OSError, asyncio.TimeoutError): + continue + + # the last attempt + task = asyncio.open_connection(addr, port, limit=limit) + reader_tgt, writer_tgt = await asyncio.wait_for(task, timeout=timeout) + return reader_tgt, writer_tgts + + async def do_direct_handshake(proto_tag, dc_idx, dec_key_and_iv=None): RESERVED_NONCE_FIRST_CHARS = [b"\xef"] RESERVED_NONCE_BEGININGS = [b"\x48\x45\x41\x44", b"\x50\x4F\x53\x54", @@ -609,18 +625,18 @@ async def do_direct_handshake(proto_tag, dc_idx, dec_key_and_iv=None): dc = TG_DATACENTERS_V4[dc_idx] try: - reader_tgt, writer_tgt = await asyncio.open_connection(dc, TG_DATACENTER_PORT, - limit=TO_CLT_BUFSIZE) - set_keepalive(writer_tgt.get_extra_info("socket")) - set_bufsizes(writer_tgt.get_extra_info("socket"), TO_CLT_BUFSIZE, TO_TG_BUFSIZE) - + reader_tgt, writer_tgt = await open_connection_tryer( + dc, TG_DATACENTER_PORT, limit=TO_CLT_BUFSIZE, timeout=TG_CONNECT_TIMEOUT) except ConnectionRefusedError as E: print_err("Got connection refused while trying to connect to", dc, TG_DATACENTER_PORT) return False - except OSError as E: + except (OSError, asyncio.TimeoutError) as E: print_err("Unable to connect to", dc, TG_DATACENTER_PORT) return False + set_keepalive(writer_tgt.get_extra_info("socket")) + set_bufsizes(writer_tgt.get_extra_info("socket"), TO_CLT_BUFSIZE, TO_TG_BUFSIZE) + while True: rnd = bytearray([random.randrange(0, 256) for i in range(HANDSHAKE_LEN)]) if rnd[:1] in RESERVED_NONCE_FIRST_CHARS: @@ -710,16 +726,18 @@ async def do_middleproxy_handshake(proto_tag, dc_idx, cl_ip, cl_port): addr, port = random.choice(TG_MIDDLE_PROXIES_V4[dc_idx]) try: - reader_tgt, writer_tgt = await asyncio.open_connection(addr, port, limit=TO_CLT_BUFSIZE) - set_keepalive(writer_tgt.get_extra_info("socket")) - set_bufsizes(writer_tgt.get_extra_info("socket"), TO_CLT_BUFSIZE, TO_TG_BUFSIZE) + reader_tgt, writer_tgt = await open_connection_tryer(addr, port, limit=TO_CLT_BUFSIZE, + timeout=TG_CONNECT_TIMEOUT) except ConnectionRefusedError as E: print_err("Got connection refused while trying to connect to", addr, port) return False - except OSError as E: + except (OSError, asyncio.TimeoutError) as E: print_err("Unable to connect to", addr, port) return False + set_keepalive(writer_tgt.get_extra_info("socket")) + set_bufsizes(writer_tgt.get_extra_info("socket"), TO_CLT_BUFSIZE, TO_TG_BUFSIZE) + writer_tgt = MTProtoFrameStreamWriter(writer_tgt, START_SEQ_NO) key_selector = PROXY_SECRET[:4] From 520a26aa89db86b0ebb8949bfd6b834cc0371401 Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Sun, 8 Jul 2018 23:52:57 +0500 Subject: [PATCH 3/5] fix typo --- mtprotoproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index cbaaec1..74b4654 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -604,7 +604,7 @@ async def open_connection_tryer(addr, port, limit, timeout, max_attempts=3): # the last attempt task = asyncio.open_connection(addr, port, limit=limit) reader_tgt, writer_tgt = await asyncio.wait_for(task, timeout=timeout) - return reader_tgt, writer_tgts + return reader_tgt, writer_tgt async def do_direct_handshake(proto_tag, dc_idx, dec_key_and_iv=None): From 8e79dacd2689b4ff1bb00c52a083e1db9124ba24 Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Tue, 10 Jul 2018 15:48:39 +0500 Subject: [PATCH 4/5] make the passive protocol detection harder --- mtprotoproxy.py | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 74b4654..28cbc1d 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -431,11 +431,6 @@ class MTProtoIntermediateFrameStreamReader(LayeredStreamReaderBase): msg_len -= 0x80000000 data = await self.upstream.readexactly(msg_len) - - if msg_len % 4 != 0: - cut_border = msg_len - (msg_len % 4) - data = data[:cut_border] - return data, extra @@ -447,6 +442,38 @@ class MTProtoIntermediateFrameStreamWriter(LayeredStreamWriterBase): return self.upstream.write(int.to_bytes(len(data), 4, 'little') + data) +class MTProtoSecureIntermediateFrameStreamReader(LayeredStreamReaderBase): + async def read(self, buf_size): + msg_len_bytes = await self.upstream.readexactly(4) + msg_len = int.from_bytes(msg_len_bytes, "little") + + extra = {} + if msg_len > 0x80000000: + extra["QUICKACK_FLAG"] = True + msg_len -= 0x80000000 + + data = await self.upstream.readexactly(msg_len) + + if msg_len % 4 != 0: + cut_border = msg_len - (msg_len % 4) + data = data[:cut_border] + + return data, extra + + +class MTProtoSecureIntermediateFrameStreamWriter(LayeredStreamWriterBase): + def write(self, data, extra={}): + MAX_PADDING_LEN = 4 + if extra.get("SIMPLE_ACK"): + # TODO: make this unpredictable + return self.upstream.write(data) + else: + padding_len = random.randrange(MAX_PADDING_LEN) + padding = bytearray([random.randrange(256) for i in range(padding_len)]) + padded_data_len_bytes = int.to_bytes(len(data) + padding_len, 4, 'little') + return self.upstream.write(padded_data_len_bytes + data + padding) + + class ProxyReqStreamReader(LayeredStreamReaderBase): async def read(self, msg): RPC_PROXY_ANS = b"\x0d\xda\x03\x44" @@ -505,6 +532,7 @@ class ProxyReqStreamWriter(LayeredStreamWriterBase): FLAG_HAS_AD_TAG = 0x8 FLAG_MAGIC = 0x1000 FLAG_EXTMODE2 = 0x20000 + FLAG_PAD = 0x8000000 FLAG_INTERMEDIATE = 0x20000000 FLAG_ABRIDGED = 0x40000000 FLAG_QUICKACK = 0x80000000 @@ -519,6 +547,8 @@ class ProxyReqStreamWriter(LayeredStreamWriterBase): flags |= FLAG_ABRIDGED elif self.proto_tag == PROTO_TAG_INTERMEDIATE: flags |= FLAG_INTERMEDIATE + elif self.proto_tag == PROTO_TAG_SECURE: + flags |= FLAG_INTERMEDIATE | FLAG_PAD if extra.get("QUICKACK_FLAG"): flags |= FLAG_QUICKACK @@ -880,9 +910,12 @@ async def handle_client(reader_clt, writer_clt): if proto_tag == PROTO_TAG_ABRIDGED: reader_clt = MTProtoCompactFrameStreamReader(reader_clt) writer_clt = MTProtoCompactFrameStreamWriter(writer_clt) - elif proto_tag in (PROTO_TAG_INTERMEDIATE, PROTO_TAG_SECURE): + elif proto_tag == PROTO_TAG_INTERMEDIATE: reader_clt = MTProtoIntermediateFrameStreamReader(reader_clt) writer_clt = MTProtoIntermediateFrameStreamWriter(writer_clt) + elif proto_tag == PROTO_TAG_SECURE: + reader_clt = MTProtoSecureIntermediateFrameStreamReader(reader_clt) + writer_clt = MTProtoSecureIntermediateFrameStreamWriter(writer_clt) else: return From c1bef686021a9aedbcb1623e190a52bb4c3f7b9c Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Tue, 10 Jul 2018 19:22:04 +0500 Subject: [PATCH 5/5] update alpine linux in the image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 3622790..174bff1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.6 +FROM alpine:3.8 RUN adduser tgproxy -u 10000 -D