diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 2787d51..e914ace 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -146,28 +146,43 @@ AD_TAG = bytes.fromhex(config.get("AD_TAG", "")) # load advanced settings # if IPv6 avaliable, use it by default PREFER_IPV6 = config.get("PREFER_IPV6", socket.has_ipv6) + # disables tg->client trafic reencryption, faster but less secure FAST_MODE = config.get("FAST_MODE", True) + # doesn't allow to connect in not-secure mode SECURE_ONLY = config.get("SECURE_ONLY", False) + +# proxy bad clients to some host to make a detection harder +PRETEND_HTTPS = config.get("PRETEND_HTTPS", False) + # delay in seconds between stats printing STATS_PRINT_PERIOD = config.get("STATS_PRINT_PERIOD", 600) + # delay in seconds between middle proxy info updates PROXY_INFO_UPDATE_PERIOD = config.get("PROXY_INFO_UPDATE_PERIOD", 24*60*60) + # max socket buffer size to the client direction, the more the faster, but more RAM hungry TO_CLT_BUFSIZE = config.get("TO_CLT_BUFSIZE", 16384) + # max socket buffer size to the telegram servers direction TO_TG_BUFSIZE = config.get("TO_TG_BUFSIZE", 65536) + # keepalive period for clients in secs CLIENT_KEEPALIVE = config.get("CLIENT_KEEPALIVE", 10*60) + # drop client after this timeout if the handshake fail CLIENT_HANDSHAKE_TIMEOUT = config.get("CLIENT_HANDSHAKE_TIMEOUT", 10) + # if client doesn't confirm data for this number of seconds, it is dropped CLIENT_ACK_TIMEOUT = config.get("CLIENT_ACK_TIMEOUT", 5*60) + # telegram servers connect timeout in seconds TG_CONNECT_TIMEOUT = config.get("TG_CONNECT_TIMEOUT", 10) + # listen address for IPv4 LISTEN_ADDR_IPV4 = config.get("LISTEN_ADDR_IPV4", "0.0.0.0") + # listen address for IPv6 LISTEN_ADDR_IPV6 = config.get("LISTEN_ADDR_IPV6", "::") @@ -581,6 +596,50 @@ class ProxyReqStreamWriter(LayeredStreamWriterBase): return self.upstream.write(full_msg) +async def proxy_bad_client(reader_clt, writer_clt, handshake): + BUF_SIZE = 8192 + CONNECT_TIMEOUT = 10 + + async def connect_reader_to_writer(rd, wr): + try: + while True: + data = await rd.read(BUF_SIZE) + + if not data: + wr.write_eof() + await wr.drain() + return + else: + wr.write(data) + await wr.drain() + except (OSError, asyncio.streams.IncompleteReadError) as e: + pass + + try: + # + ADDR = "www.cloudflare.com" + PORT = 443 + task = asyncio.open_connection(ADDR, PORT, limit=BUF_SIZE) + reader_srv, writer_srv = await asyncio.wait_for(task, timeout=CONNECT_TIMEOUT) + writer_srv.write(handshake) + except ConnectionRefusedError as E: + return False + except (OSError, asyncio.TimeoutError) as E: + return False + + srv_to_clt = connect_reader_to_writer(reader_srv, writer_clt) + clt_to_srv = connect_reader_to_writer(reader_clt, writer_srv) + task_srv_to_clt = asyncio.ensure_future(srv_to_clt) + task_clt_to_srv = asyncio.ensure_future(clt_to_srv) + + await asyncio.wait([task_srv_to_clt, task_clt_to_srv], return_when=asyncio.FIRST_COMPLETED) + + task_srv_to_clt.cancel() + task_clt_to_srv.cancel() + + writer_srv.transport.abort() + + async def handle_handshake(reader, writer): handshake = await reader.readexactly(HANDSHAKE_LEN) @@ -612,10 +671,14 @@ async def handle_handshake(reader, writer): writer = CryptoWrappedStreamWriter(writer, encryptor) return reader, writer, proto_tag, user, dc_idx, enc_key + enc_iv - EMPTY_READ_BUF_SIZE = 4096 - while await reader.read(EMPTY_READ_BUF_SIZE): - # just consume all the data - pass + if not PRETEND_HTTPS: + EMPTY_READ_BUF_SIZE = 4096 + while await reader.read(EMPTY_READ_BUF_SIZE): + # just consume all the data + pass + else: + # proxy client to some host + await proxy_bad_client(reader, writer, handshake) return False