Skip to content

Commit

Permalink
protect from time skewing. The proxy protocol is very sensible to clo…
Browse files Browse the repository at this point in the history
…ck skew. If the skew is detected, disable advertising, making the connection directly to tg servers, instead of middle proxies
  • Loading branch information
alexbers committed May 11, 2019
1 parent af8c102 commit eba7f9b
Showing 1 changed file with 63 additions and 18 deletions.
81 changes: 63 additions & 18 deletions mtprotoproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import urllib.request
import collections
import time
import datetime
import hashlib
import random
import binascii
Expand Down Expand Up @@ -55,6 +56,9 @@
# delay in seconds between middle proxy info updates
PROXY_INFO_UPDATE_PERIOD = config.get("PROXY_INFO_UPDATE_PERIOD", 24*60*60)

# delay in seconds between time getting, zero means disabled
GET_TIME_PERIOD = config.get("GET_TIME_PERIOD", 10*60)

# max socket buffer size to the client direction, the more the faster, but more RAM hungry
# can be the tuple (low, users_margin, high) for the adaptive case. If no much users, use high
TO_CLT_BUFSIZE = config.get("TO_CLT_BUFSIZE", (16384, 100, 131072))
Expand All @@ -80,6 +84,7 @@
# listen address for IPv6
LISTEN_ADDR_IPV6 = config.get("LISTEN_ADDR_IPV6", "::")


TG_DATACENTER_PORT = 443

TG_DATACENTERS_V4 = [
Expand Down Expand Up @@ -1084,27 +1089,63 @@ async def stats_printer():
print(flush=True)


async def update_middle_proxy_info():
async def make_https_req(url):
# returns resp body
SSL_PORT = 443
url_data = urllib.parse.urlparse(url)

HTTP_REQ_TEMPLATE = "\r\n".join(["GET %s HTTP/1.1", "Host: core.telegram.org",
"Connection: close"]) + "\r\n\r\n"
reader, writer = await asyncio.open_connection(url_data.netloc, SSL_PORT, ssl=True)
req = HTTP_REQ_TEMPLATE % urllib.parse.quote(url_data.path)
writer.write(req.encode("utf8"))
data = await reader.read()
writer.close()

headers, body = data.split(b"\r\n\r\n", 1)
return body
async def make_https_req(url, host="core.telegram.org"):
""" Make request, return resp body and headers. """
SSL_PORT = 443
url_data = urllib.parse.urlparse(url)

HTTP_REQ_TEMPLATE = "\r\n".join(["GET %s HTTP/1.1", "Host: %s",
"Connection: close"]) + "\r\n\r\n"
reader, writer = await asyncio.open_connection(url_data.netloc, SSL_PORT, ssl=True)
req = HTTP_REQ_TEMPLATE % (urllib.parse.quote(url_data.path), host)
writer.write(req.encode("utf8"))
data = await reader.read()
writer.close()

headers, body = data.split(b"\r\n\r\n", 1)
return headers, body


async def get_srv_time():
global USE_MIDDLE_PROXY
TIME_SYNC_ADDR = "https://core.telegram.org/getProxySecret"
MAX_TIME_SKEW = 30

want_to_reenable_advertising = False
while True:
try:
headers, secret = await make_https_req(TIME_SYNC_ADDR)

for line in headers.split(b"\r\n"):
if not line.startswith(b"Date: "):
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()
time_diff = (now_time-srv_time).total_seconds()
if USE_MIDDLE_PROXY and abs(time_diff) > MAX_TIME_SKEW:
print_err("Time skew detected, please set the clock")
print_err("Server time:", srv_time, "your time:", now_time)
print_err("Disabling advertising to continue serving")

USE_MIDDLE_PROXY = False
want_to_reenable_advertising = True
elif want_to_reenable_advertising and abs(time_diff) <= MAX_TIME_SKEW:
print_err("Time is ok, reenabling advertising")
USE_MIDDLE_PROXY = True
want_to_reenable_advertising = False

except Exception as E:
print_err("Error getting server time", E)

await asyncio.sleep(GET_TIME_PERIOD)


async def update_middle_proxy_info():
async def get_new_proxies(url):
PROXY_REGEXP = re.compile(r"proxy_for\s+(-?\d+)\s+(.+):(\d+)\s*;")
ans = {}
body = await make_https_req(url)
headers, body = await make_https_req(url)

fields = PROXY_REGEXP.findall(body.decode("utf8"))
if fields:
Expand Down Expand Up @@ -1144,7 +1185,7 @@ async def get_new_proxies(url):
print_err("Error updating middle proxy list for IPv6:", E)

try:
secret = await make_https_req(PROXY_SECRET_ADDR)
headers, secret = await make_https_req(PROXY_SECRET_ADDR)
if not secret:
raise Exception("no secret")
if secret != PROXY_SECRET:
Expand Down Expand Up @@ -1261,6 +1302,10 @@ def main():
middle_proxy_updater_task = asyncio.Task(update_middle_proxy_info())
asyncio.ensure_future(middle_proxy_updater_task)

if GET_TIME_PERIOD:
time_get_task = asyncio.Task(get_srv_time())
asyncio.ensure_future(time_get_task)

reuse_port = hasattr(socket, "SO_REUSEPORT")

task_v4 = asyncio.start_server(handle_client_wrapper, LISTEN_ADDR_IPV4, PORT,
Expand Down

0 comments on commit eba7f9b

Please sign in to comment.