From 6a7a0096b4477b55689fcfe0376f98825862b764 Mon Sep 17 00:00:00 2001 From: "Foster \"Forst\" Snowhill" Date: Mon, 28 May 2018 20:42:20 +0300 Subject: [PATCH 1/6] Add support for listening on IPv6 addresses --- mtprotoproxy.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 9531f92..6d4ea38 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -81,7 +81,7 @@ async def handle_handshake(reader, writer): encryptor = create_aes(key=enc_key, iv=int.from_bytes(enc_iv, "big")) decrypted = decryptor.decrypt(handshake) - + check_val = decrypted[MAGIC_VAL_POS:MAGIC_VAL_POS+4] if check_val != MAGIC_VAL_TO_CHECK: continue @@ -134,7 +134,7 @@ async def do_handshake(dc, dec_key_and_iv=None): enc_key_and_iv = rnd[SKIP_LEN:SKIP_LEN+KEY_LEN+IV_LEN] enc_key, enc_iv = enc_key_and_iv[:KEY_LEN], enc_key_and_iv[KEY_LEN:] encryptor = create_aes(key=enc_key, iv=int.from_bytes(enc_iv, "big")) - + rnd_enc = rnd[:MAGIC_VAL_POS] + encryptor.encrypt(rnd)[MAGIC_VAL_POS:] writer_tgt.write(rnd_enc) @@ -240,9 +240,15 @@ def main(): loop = asyncio.get_event_loop() stats_printer_task = asyncio.Task(stats_printer()) asyncio.ensure_future(stats_printer_task) - task = asyncio.start_server(handle_client_wrapper, - "0.0.0.0", PORT, loop=loop) - server = loop.run_until_complete(task) + + task_v4 = asyncio.start_server(handle_client_wrapper, + '0.0.0.0', PORT, loop=loop) + server_v4 = loop.run_until_complete(task_v4) + + if socket.has_ipv6: + task_v6 = asyncio.start_server(handle_client_wrapper, + '::', PORT, loop=loop) + server_v6 = loop.run_until_complete(task_v6) try: loop.run_forever() @@ -251,8 +257,13 @@ def main(): stats_printer_task.cancel() - server.close() - loop.run_until_complete(server.wait_closed()) + server_v4.close() + loop.run_until_complete(server_v4.wait_closed()) + + if socket.has_ipv6: + server_v6.close() + loop.run_until_complete(server_v6.wait_closed()) + loop.close() From 9ed7cb6bc188938a80f5b728170cccc048e3fc94 Mon Sep 17 00:00:00 2001 From: "Foster \"Forst\" Snowhill" Date: Mon, 28 May 2018 20:53:24 +0300 Subject: [PATCH 2/6] Add optional support for IPv6 datacenter addresses --- config.py | 1 + mtprotoproxy.py | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/config.py b/config.py index 4f1bb1a..a5df43f 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,5 @@ PORT = 3256 +PREFER_IPV6 = False # name -> secret (32 hex chars) USERS = { diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 6d4ea38..fa56ff1 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -25,13 +25,21 @@ except ModuleNotFoundError: return pyaes.AESModeOfOperationCTR(key, ctr) -from config import PORT, USERS +from config import PORT, PREFER_IPV6, USERS -TG_DATACENTERS = [ +TG_DATACENTERS_V4 = [ "149.154.175.50", "149.154.167.51", "149.154.175.100", "149.154.167.91", "149.154.171.5" ] +TG_DATACENTERS_V6 = [ + "2001:0b28:f23d:f001:0000:0000:0000:000a", + "2001:067c:04e8:f002:0000:0000:0000:000a", + "2001:0b28:f23d:f003:0000:0000:0000:000a", + "2001:067c:04e8:f004:0000:0000:0000:000a", + "2001:0b28:f23f:f005:0000:0000:0000:000a", +] + TG_DATACENTER_PORT = 443 # disables tg->client trafic reencryption, faster but less secure @@ -88,10 +96,13 @@ async def handle_handshake(reader, writer): dc_idx = abs(int.from_bytes(decrypted[60:62], "little", signed=True)) - 1 - if dc_idx < 0 or dc_idx >= len(TG_DATACENTERS): + if dc_idx < 0 or dc_idx >= len(TG_DATACENTERS_V4) or dc_idx >= len(TG_DATACENTERS_V6): continue - dc = TG_DATACENTERS[dc_idx] + if PREFER_IPV6: + dc = TG_DATACENTERS_V6[dc_idx] + else: + dc = TG_DATACENTERS_V4[dc_idx] return encryptor, decryptor, user, dc, enc_key + enc_iv return False From 994679982b74406b5d17bb46a8cbce4a24a657a8 Mon Sep 17 00:00:00 2001 From: "Foster \"Forst\" Snowhill" Date: Mon, 28 May 2018 21:30:34 +0300 Subject: [PATCH 3/6] Use external service to retrieve host's IP address. Should display a proper IP for users behind NAT (like a home router), for example. --- mtprotoproxy.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index fa56ff1..0b308a7 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -3,6 +3,7 @@ import asyncio import socket import urllib.parse +import urllib.request import collections import time import hashlib @@ -226,17 +227,12 @@ async def stats_printer(): def print_tg_info(): - my_ip = socket.gethostbyname(socket.gethostname()) - - octets = [int(o) for o in my_ip.split(".")] - - ip_is_local = (len(octets) == 4 and ( - octets[0] in [127, 10] or - octets[0:2] == [192, 168] or - (octets[0] == 172 and 16 <= octets[1] <= 31))) - - if ip_is_local: - my_ip = "YOUR_IP" + try: + with urllib.request.urlopen('https://ifconfig.co/ip') as f: + if f.status != 200: raise Exception("Invalid status code") + my_ip = f.read().strip() + except: + my_ip = 'YOUR_IP' for user, secret in USERS.items(): params = { From 0738b4395a7746884f07c7d749c75c9255143fcc Mon Sep 17 00:00:00 2001 From: "Foster \"Forst\" Snowhill" Date: Mon, 28 May 2018 21:32:51 +0300 Subject: [PATCH 4/6] Print user ID for every proxy link --- mtprotoproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 0b308a7..74c06e3 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -238,7 +238,7 @@ def print_tg_info(): params = { "server": my_ip, "port": PORT, "secret": secret } - print("tg://proxy?" + urllib.parse.urlencode(params), flush=True) + print("{}: tg://proxy?{}".format(user, urllib.parse.urlencode(params)), flush=True) def main(): From e81c3535c30e036cb23230658ce3d856592331ff Mon Sep 17 00:00:00 2001 From: "Foster \"Forst\" Snowhill" Date: Mon, 28 May 2018 21:43:28 +0300 Subject: [PATCH 5/6] Fix encoding of IPv6 addresses, make colon safe --- mtprotoproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 74c06e3..1457a6d 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -238,7 +238,7 @@ def print_tg_info(): params = { "server": my_ip, "port": PORT, "secret": secret } - print("{}: tg://proxy?{}".format(user, urllib.parse.urlencode(params)), flush=True) + print("{}: tg://proxy?{}".format(user, urllib.parse.urlencode(params, safe=':')), flush=True) def main(): From 0fe7ba406ed85cbdfd4b89d67cc7fcc94520d87c Mon Sep 17 00:00:00 2001 From: "Foster \"Forst\" Snowhill" Date: Tue, 29 May 2018 16:33:50 +0300 Subject: [PATCH 6/6] Make proxy link order consistent, sort by username --- mtprotoproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mtprotoproxy.py b/mtprotoproxy.py index 1457a6d..9d4a59e 100755 --- a/mtprotoproxy.py +++ b/mtprotoproxy.py @@ -234,7 +234,7 @@ def print_tg_info(): except: my_ip = 'YOUR_IP' - for user, secret in USERS.items(): + for user, secret in sorted(USERS.items(), key=lambda x: x[0]): params = { "server": my_ip, "port": PORT, "secret": secret }