mirror of
https://github.com/alexbers/mtprotoproxy.git
synced 2026-03-14 07:13:09 +00:00
Compare commits
23 Commits
v1.1.0
...
34f743858c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34f743858c | ||
|
|
375fee1535 | ||
|
|
e0ea17978c | ||
|
|
8bb885ada5 | ||
|
|
bc841cff48 | ||
|
|
c89479000f | ||
|
|
74711c4212 | ||
|
|
51b2482dec | ||
|
|
87f4370927 | ||
|
|
a978eae922 | ||
|
|
88c8c57a44 | ||
|
|
446682521b | ||
|
|
b26873176a | ||
|
|
6e8e8b63b2 | ||
|
|
3b4f239cc1 | ||
|
|
0283d6264a | ||
|
|
15a8f607ca | ||
|
|
6076db9f8c | ||
|
|
6560a6c1d2 | ||
|
|
24479e68ab | ||
|
|
6ecf0ec9ac | ||
|
|
18a80e52cd | ||
|
|
ea3b8a44c3 |
14
Dockerfile
14
Dockerfile
@@ -1,16 +1,14 @@
|
||||
FROM python:3.8-slim-buster
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt-get update && apt-get install -y libcap2-bin && rm -rf /var/lib/apt/lists/*
|
||||
RUN setcap cap_net_bind_service=+ep /usr/local/bin/python3.8
|
||||
|
||||
RUN pip3 --no-cache-dir install cryptography uvloop
|
||||
|
||||
COPY mtprotoproxy.py config.py /home/tgproxy/
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y python3 python3-uvloop python3-cryptography python3-socks libcap2-bin ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
RUN setcap cap_net_bind_service=+ep /usr/bin/python3.12
|
||||
|
||||
RUN useradd tgproxy -u 10000
|
||||
RUN chown -R tgproxy:tgproxy /home/tgproxy
|
||||
|
||||
USER tgproxy
|
||||
|
||||
WORKDIR /home/tgproxy/
|
||||
|
||||
COPY --chown=tgproxy mtprotoproxy.py config.py /home/tgproxy/
|
||||
|
||||
CMD ["python3", "mtprotoproxy.py"]
|
||||
|
||||
@@ -7,4 +7,10 @@ services:
|
||||
volumes:
|
||||
- ./config.py:/home/tgproxy/config.py
|
||||
- ./mtprotoproxy.py:/home/tgproxy/mtprotoproxy.py
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-file: "10"
|
||||
max-size: "10m"
|
||||
# mem_limit: 1024m
|
||||
|
||||
@@ -36,10 +36,10 @@ TG_DATACENTERS_V6 = [
|
||||
# This list will be updated in the runtime
|
||||
TG_MIDDLE_PROXIES_V4 = {
|
||||
1: [("149.154.175.50", 8888)], -1: [("149.154.175.50", 8888)],
|
||||
2: [("149.154.162.38", 80)], -2: [("149.154.162.38", 80)],
|
||||
2: [("149.154.161.144", 8888)], -2: [("149.154.161.144", 8888)],
|
||||
3: [("149.154.175.100", 8888)], -3: [("149.154.175.100", 8888)],
|
||||
4: [("91.108.4.136", 8888)], -4: [("149.154.165.109", 8888)],
|
||||
5: [("91.108.56.181", 8888)], -5: [("91.108.56.181", 8888)]
|
||||
5: [("91.108.56.183", 8888)], -5: [("91.108.56.183", 8888)]
|
||||
}
|
||||
|
||||
TG_MIDDLE_PROXIES_V6 = {
|
||||
@@ -47,7 +47,7 @@ TG_MIDDLE_PROXIES_V6 = {
|
||||
2: [("2001:67c:04e8:f002::d", 80)], -2: [("2001:67c:04e8:f002::d", 80)],
|
||||
3: [("2001:b28:f23d:f003::d", 8888)], -3: [("2001:b28:f23d:f003::d", 8888)],
|
||||
4: [("2001:67c:04e8:f004::d", 8888)], -4: [("2001:67c:04e8:f004::d", 8888)],
|
||||
5: [("2001:b28:f23f:f005::d", 8888)], -5: [("2001:67c:04e8:f004::d", 8888)]
|
||||
5: [("2001:b28:f23f:f005::d", 8888)], -5: [("2001:b28:f23f:f005::d", 8888)]
|
||||
}
|
||||
|
||||
PROXY_SECRET = bytes.fromhex(
|
||||
@@ -62,7 +62,6 @@ PREKEY_LEN = 32
|
||||
KEY_LEN = 32
|
||||
IV_LEN = 16
|
||||
HANDSHAKE_LEN = 64
|
||||
TLS_HANDSHAKE_LEN = 1 + 2 + 2 + 512
|
||||
PROTO_TAG_POS = 56
|
||||
DC_IDX_POS = 60
|
||||
|
||||
@@ -196,6 +195,9 @@ def init_config():
|
||||
# the next host to forward bad clients
|
||||
conf_dict.setdefault("MASK_HOST", conf_dict["TLS_DOMAIN"])
|
||||
|
||||
# set the home domain for the proxy, has an influence only on the log message
|
||||
conf_dict.setdefault("MY_DOMAIN", False)
|
||||
|
||||
# the next host's port to forward bad clients
|
||||
conf_dict.setdefault("MASK_PORT", 443)
|
||||
|
||||
@@ -655,7 +657,7 @@ class CryptoWrappedStreamReader(LayeredStreamReaderBase):
|
||||
|
||||
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):
|
||||
@@ -865,6 +867,7 @@ class ProxyReqStreamReader(LayeredStreamReaderBase):
|
||||
RPC_PROXY_ANS = b"\x0d\xda\x03\x44"
|
||||
RPC_CLOSE_EXT = b"\xa2\x34\xb6\x5e"
|
||||
RPC_SIMPLE_ACK = b"\x9b\x40\xac\x3b"
|
||||
RPC_UNKNOWN = b'\xdf\xa2\x30\x57'
|
||||
|
||||
data = await self.upstream.read(1)
|
||||
|
||||
@@ -883,8 +886,11 @@ class ProxyReqStreamReader(LayeredStreamReaderBase):
|
||||
conn_id, confirm = data[4:12], data[12:16]
|
||||
return confirm, {"SIMPLE_ACK": True}
|
||||
|
||||
if ans_type == RPC_UNKNOWN:
|
||||
return b"", {"SKIP_SEND": True}
|
||||
|
||||
print_err("unknown rpc ans type:", ans_type)
|
||||
return b""
|
||||
return b"", {"SKIP_SEND": True}
|
||||
|
||||
|
||||
class ProxyReqStreamWriter(LayeredStreamWriterBase):
|
||||
@@ -1229,7 +1235,7 @@ async def handle_handshake(reader, writer):
|
||||
global last_client_ips
|
||||
global last_clients_with_same_handshake
|
||||
|
||||
TLS_START_BYTES = b"\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03"
|
||||
TLS_START_BYTES = b"\x16\x03\x01"
|
||||
|
||||
if writer.transport.is_closing() or writer.get_extra_info("peername") is None:
|
||||
return False
|
||||
@@ -1255,7 +1261,13 @@ async def handle_handshake(reader, writer):
|
||||
break
|
||||
|
||||
if is_tls_handshake:
|
||||
handshake += await reader.readexactly(TLS_HANDSHAKE_LEN - len(handshake))
|
||||
handshake += await reader.readexactly(2)
|
||||
tls_handshake_len = int.from_bytes(handshake[-2:], "big")
|
||||
if tls_handshake_len < 512:
|
||||
is_tls_handshake = False
|
||||
|
||||
if is_tls_handshake:
|
||||
handshake += await reader.readexactly(tls_handshake_len)
|
||||
tls_handshake_result = await handle_fake_tls_handshake(handshake, reader, writer, peer)
|
||||
|
||||
if not tls_handshake_result:
|
||||
@@ -1352,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)
|
||||
@@ -1567,6 +1579,9 @@ async def tg_connect_reader_to_writer(rd, wr, user, rd_buf_size, is_upstream):
|
||||
else:
|
||||
extra = {}
|
||||
|
||||
if extra.get("SKIP_SEND"):
|
||||
continue
|
||||
|
||||
if not data:
|
||||
wr.write_eof()
|
||||
await wr.drain()
|
||||
@@ -1722,6 +1737,7 @@ def make_metrics_pkt(metrics):
|
||||
|
||||
pkt_header_list = []
|
||||
pkt_header_list.append("HTTP/1.1 200 OK")
|
||||
pkt_header_list.append("Connection: close")
|
||||
pkt_header_list.append("Content-Length: %d" % len(pkt_body))
|
||||
pkt_header_list.append("Content-Type: text/plain; version=0.0.4; charset=utf-8")
|
||||
pkt_header_list.append("Date: %s" % time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()))
|
||||
@@ -1914,6 +1930,16 @@ async def get_encrypted_cert(host, port, server_name):
|
||||
if record3_type != 23:
|
||||
return b""
|
||||
|
||||
if len(record3) < MIN_CERT_LEN:
|
||||
record4_type, record4 = await get_tls_record(reader)
|
||||
if record4_type != 23:
|
||||
return b""
|
||||
msg = ("The MASK_HOST %s sent some TLS record before certificate record, this makes the " +
|
||||
"proxy more detectable") % config.MASK_HOST
|
||||
print_err(msg)
|
||||
|
||||
return record4
|
||||
|
||||
return record3
|
||||
|
||||
|
||||
@@ -2082,6 +2108,10 @@ def init_ip_info():
|
||||
my_ip_info["ipv4"] = get_ip_from_url(IPV4_URL1) or get_ip_from_url(IPV4_URL2)
|
||||
my_ip_info["ipv6"] = get_ip_from_url(IPV6_URL1) or get_ip_from_url(IPV6_URL2)
|
||||
|
||||
# the server can return ipv4 address instead of ipv6
|
||||
if my_ip_info["ipv6"] and ":" not in my_ip_info["ipv6"]:
|
||||
my_ip_info["ipv6"] = None
|
||||
|
||||
if my_ip_info["ipv6"] and (config.PREFER_IPV6 or not my_ip_info["ipv4"]):
|
||||
print_err("IPv6 found, using it for external communication")
|
||||
|
||||
@@ -2103,9 +2133,12 @@ def print_tg_info():
|
||||
print("Since you have TLS only mode enabled the best port is 443", flush=True)
|
||||
print_default_warning = True
|
||||
|
||||
ip_addrs = [ip for ip in my_ip_info.values() if ip]
|
||||
if not ip_addrs:
|
||||
ip_addrs = ["YOUR_IP"]
|
||||
if not config.MY_DOMAIN:
|
||||
ip_addrs = [ip for ip in my_ip_info.values() if ip]
|
||||
if not ip_addrs:
|
||||
ip_addrs = ["YOUR_IP"]
|
||||
else:
|
||||
ip_addrs = [config.MY_DOMAIN]
|
||||
|
||||
proxy_links = []
|
||||
|
||||
@@ -2276,21 +2309,21 @@ def create_servers(loop):
|
||||
def create_utilitary_tasks(loop):
|
||||
tasks = []
|
||||
|
||||
stats_printer_task = asyncio.Task(stats_printer())
|
||||
stats_printer_task = asyncio.Task(stats_printer(), loop=loop)
|
||||
tasks.append(stats_printer_task)
|
||||
|
||||
if config.USE_MIDDLE_PROXY:
|
||||
middle_proxy_updater_task = asyncio.Task(update_middle_proxy_info())
|
||||
middle_proxy_updater_task = asyncio.Task(update_middle_proxy_info(), loop=loop)
|
||||
tasks.append(middle_proxy_updater_task)
|
||||
|
||||
if config.GET_TIME_PERIOD:
|
||||
time_get_task = asyncio.Task(get_srv_time())
|
||||
time_get_task = asyncio.Task(get_srv_time(), loop=loop)
|
||||
tasks.append(time_get_task)
|
||||
|
||||
get_cert_len_task = asyncio.Task(get_mask_host_cert_len())
|
||||
get_cert_len_task = asyncio.Task(get_mask_host_cert_len(), loop=loop)
|
||||
tasks.append(get_cert_len_task)
|
||||
|
||||
clear_resolving_cache_task = asyncio.Task(clear_ip_resolving_cache())
|
||||
clear_resolving_cache_task = asyncio.Task(clear_ip_resolving_cache(), loop=loop)
|
||||
tasks.append(clear_resolving_cache_task)
|
||||
|
||||
return tasks
|
||||
@@ -2312,9 +2345,10 @@ def main():
|
||||
|
||||
if sys.platform == "win32":
|
||||
loop = asyncio.ProactorEventLoop()
|
||||
asyncio.set_event_loop(loop)
|
||||
else:
|
||||
loop = asyncio.new_event_loop()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.set_exception_handler(loop_exception_handler)
|
||||
|
||||
utilitary_tasks = create_utilitary_tasks(loop)
|
||||
|
||||
Reference in New Issue
Block a user