py-mtproxy-lib 0.0.2__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/PKG-INFO +2 -2
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/mtproxy/cli.py +56 -20
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/mtproxy/server.py +30 -56
- py_mtproxy_lib-0.0.4/mtproxy/utils.py +96 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/py_mtproxy_lib.egg-info/PKG-INFO +2 -2
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/py_mtproxy_lib.egg-info/SOURCES.txt +1 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/setup.py +2 -2
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/README.md +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/mtproxy/__init__.py +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/mtproxy/config.py +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/mtproxy/daemon.py +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/mtproxy/logger.py +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/mtproxy/stats.py +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/py_mtproxy_lib.egg-info/dependency_links.txt +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/py_mtproxy_lib.egg-info/entry_points.txt +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/py_mtproxy_lib.egg-info/requires.txt +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/py_mtproxy_lib.egg-info/top_level.txt +0 -0
- {py_mtproxy_lib-0.0.2 → py_mtproxy_lib-0.0.4}/setup.cfg +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-mtproxy-lib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Production-ready MTProto proxy server library with multi-secret support, statistics, and daemon mode
|
|
5
5
|
Home-page: https://github.com/twosleepynights0x1/py-mtproxy-lib
|
|
6
|
-
Author:
|
|
6
|
+
Author: AneK
|
|
7
7
|
Author-email: tashova28@gmail.com
|
|
8
8
|
Project-URL: Bug Reports, https://github.com/twosleepynights0x1/py-mtproxy-lib
|
|
9
9
|
Keywords: mtproto telegram proxy vpn daemon docker
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
"""CLI интерфейс"""
|
|
2
|
-
|
|
3
1
|
import argparse
|
|
4
2
|
import sys
|
|
5
3
|
import os
|
|
6
4
|
from .config import ProxyConfig
|
|
7
5
|
from .server import MTProxyServer
|
|
8
|
-
from .utils import generate_secret
|
|
6
|
+
from .utils import generate_secret, find_free_port, get_public_ip, get_common_ports
|
|
9
7
|
from .daemon import Daemon
|
|
10
8
|
from .logger import setup_logger
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
def start_server(config: ProxyConfig, foreground: bool = False):
|
|
14
|
-
"""Запуск сервера"""
|
|
15
|
-
# Настройка логгера
|
|
16
12
|
logger = setup_logger(
|
|
17
13
|
level=config.log_level,
|
|
18
14
|
log_file=config.log_file
|
|
@@ -32,7 +28,6 @@ def start_server(config: ProxyConfig, foreground: bool = False):
|
|
|
32
28
|
|
|
33
29
|
|
|
34
30
|
def main():
|
|
35
|
-
"""Основная функция CLI"""
|
|
36
31
|
parser = argparse.ArgumentParser(
|
|
37
32
|
description="MTProto Proxy Server",
|
|
38
33
|
prog="mtproxy"
|
|
@@ -53,10 +48,16 @@ def main():
|
|
|
53
48
|
|
|
54
49
|
parser.add_argument(
|
|
55
50
|
"-p", "--port",
|
|
56
|
-
help="Port to listen on",
|
|
51
|
+
help="Port to listen on (auto-find if not specified)",
|
|
57
52
|
type=int
|
|
58
53
|
)
|
|
59
54
|
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"--auto-port",
|
|
57
|
+
help="Automatically find free port",
|
|
58
|
+
action="store_true"
|
|
59
|
+
)
|
|
60
|
+
|
|
60
61
|
parser.add_argument(
|
|
61
62
|
"--no-fake-tls",
|
|
62
63
|
help="Disable Fake TLS masking",
|
|
@@ -106,9 +107,21 @@ def main():
|
|
|
106
107
|
action="store_true"
|
|
107
108
|
)
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"--show-ip",
|
|
112
|
+
help="Show public IP address",
|
|
113
|
+
action="store_true"
|
|
114
|
+
)
|
|
111
115
|
|
|
116
|
+
parser.add_argument(
|
|
117
|
+
"--find-port",
|
|
118
|
+
help="Find free port in range",
|
|
119
|
+
type=str,
|
|
120
|
+
nargs='?',
|
|
121
|
+
const="5000-10000"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
subparsers = parser.add_subparsers(dest="command", help="Daemon commands")
|
|
112
125
|
subparsers.add_parser("start", help="Start daemon")
|
|
113
126
|
subparsers.add_parser("stop", help="Stop daemon")
|
|
114
127
|
subparsers.add_parser("restart", help="Restart daemon")
|
|
@@ -116,12 +129,27 @@ def main():
|
|
|
116
129
|
|
|
117
130
|
args = parser.parse_args()
|
|
118
131
|
|
|
119
|
-
# Генерация секрета
|
|
120
132
|
if args.generate_secret:
|
|
121
133
|
print(generate_secret())
|
|
122
134
|
sys.exit(0)
|
|
123
135
|
|
|
124
|
-
|
|
136
|
+
if args.show_ip:
|
|
137
|
+
ip = get_public_ip()
|
|
138
|
+
print(ip if ip else "Could not detect public IP")
|
|
139
|
+
sys.exit(0)
|
|
140
|
+
|
|
141
|
+
if args.find_port:
|
|
142
|
+
try:
|
|
143
|
+
if '-' in args.find_port:
|
|
144
|
+
start, end = map(int, args.find_port.split('-'))
|
|
145
|
+
else:
|
|
146
|
+
start, end = 5000, 10000
|
|
147
|
+
port = find_free_port(start, end)
|
|
148
|
+
print(f"Free port found: {port}")
|
|
149
|
+
except Exception as e:
|
|
150
|
+
print(f"Error: {e}")
|
|
151
|
+
sys.exit(0)
|
|
152
|
+
|
|
125
153
|
config = None
|
|
126
154
|
if os.path.exists(args.config):
|
|
127
155
|
try:
|
|
@@ -132,9 +160,16 @@ def main():
|
|
|
132
160
|
if not config:
|
|
133
161
|
config = ProxyConfig()
|
|
134
162
|
|
|
135
|
-
# Применение CLI аргументов
|
|
136
163
|
if args.port:
|
|
137
164
|
config.port = args.port
|
|
165
|
+
elif args.auto_port or not config.port:
|
|
166
|
+
try:
|
|
167
|
+
config.port = find_free_port(5000, 10000, get_common_ports())
|
|
168
|
+
print(f"Auto-selected free port: {config.port}")
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(f"Error finding free port: {e}", file=sys.stderr)
|
|
171
|
+
sys.exit(1)
|
|
172
|
+
|
|
138
173
|
if args.no_fake_tls:
|
|
139
174
|
config.enable_fake_tls = False
|
|
140
175
|
if args.pid_file:
|
|
@@ -146,25 +181,27 @@ def main():
|
|
|
146
181
|
if args.stats_interval:
|
|
147
182
|
config.stats_interval = args.stats_interval
|
|
148
183
|
|
|
149
|
-
# Добавление секретов
|
|
150
184
|
if args.secret:
|
|
151
185
|
for secret in args.secret:
|
|
152
186
|
config.add_secret(secret)
|
|
153
187
|
|
|
154
|
-
# Если нет секретов, создаем один
|
|
155
188
|
if not config.secrets:
|
|
156
189
|
config.add_secret()
|
|
157
190
|
print(f"Generated default secret: {list(config.secrets.keys())[0]}")
|
|
158
191
|
|
|
159
|
-
# Показ ссылок
|
|
160
192
|
if args.show_links:
|
|
161
|
-
|
|
193
|
+
from .utils import get_public_ip
|
|
194
|
+
ip = get_public_ip() or "YOUR_SERVER_IP"
|
|
195
|
+
print(f"Server IP: {ip}")
|
|
196
|
+
print(f"Port: {config.port}")
|
|
162
197
|
print("Connection links:")
|
|
163
|
-
for
|
|
164
|
-
|
|
198
|
+
for secret, tag in config.secrets.items():
|
|
199
|
+
link = f"tg://proxy?server={ip}&port={config.port}&secret={secret}"
|
|
200
|
+
if tag:
|
|
201
|
+
link += f"&tag={tag}"
|
|
202
|
+
print(f" {link}")
|
|
165
203
|
sys.exit(0)
|
|
166
204
|
|
|
167
|
-
# Команды демона
|
|
168
205
|
daemon = Daemon(
|
|
169
206
|
pidfile=config.pid_file or '/var/run/mtproxy.pid',
|
|
170
207
|
stdout=config.log_file or '/dev/null',
|
|
@@ -181,7 +218,6 @@ def main():
|
|
|
181
218
|
print(daemon.status())
|
|
182
219
|
sys.exit(0)
|
|
183
220
|
|
|
184
|
-
# Запуск в foreground или daemon
|
|
185
221
|
if args.daemon:
|
|
186
222
|
daemon.start(start_server, config)
|
|
187
223
|
else:
|
|
@@ -1,28 +1,17 @@
|
|
|
1
|
-
"""Основной класс MTProto прокси сервера"""
|
|
2
|
-
|
|
3
1
|
import asyncio
|
|
4
2
|
import uuid
|
|
5
3
|
import signal
|
|
6
4
|
import time
|
|
7
|
-
from typing import Optional, Dict
|
|
5
|
+
from typing import Optional, Dict
|
|
8
6
|
from .config import ProxyConfig
|
|
9
7
|
from .stats import ProxyStats
|
|
10
8
|
from .logger import get_logger
|
|
11
|
-
from .utils import
|
|
9
|
+
from .utils import get_external_ip, get_public_ip
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
class MTProxyServer:
|
|
15
|
-
"""
|
|
16
|
-
MTProto прокси сервер с поддержкой нескольких секретов и статистики
|
|
17
|
-
"""
|
|
18
13
|
|
|
19
14
|
def __init__(self, config: ProxyConfig):
|
|
20
|
-
"""
|
|
21
|
-
Инициализация прокси сервера
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
config: Конфигурация прокси
|
|
25
|
-
"""
|
|
26
15
|
self.config = config
|
|
27
16
|
self.logger = get_logger()
|
|
28
17
|
self.stats = ProxyStats()
|
|
@@ -32,22 +21,30 @@ class MTProxyServer:
|
|
|
32
21
|
self._conn_counter = 0
|
|
33
22
|
|
|
34
23
|
def _get_conn_id(self) -> str:
|
|
35
|
-
"""Генерация ID для соединения"""
|
|
36
24
|
self._conn_counter += 1
|
|
37
25
|
return f"{self._conn_counter}_{uuid.uuid4().hex[:8]}"
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
def get_connection_link(self, custom_ip: str = None) -> str:
|
|
28
|
+
ip = custom_ip or get_public_ip() or get_external_ip() or "YOUR_SERVER_IP"
|
|
29
|
+
secret = list(self.config.secrets.keys())[0] if self.config.secrets else ""
|
|
30
|
+
return f"tg://proxy?server={ip}&port={self.config.port}&secret={secret}"
|
|
31
|
+
|
|
32
|
+
def get_all_links(self, custom_ip: str = None) -> Dict[str, str]:
|
|
33
|
+
ip = custom_ip or get_public_ip() or get_external_ip() or "YOUR_SERVER_IP"
|
|
34
|
+
links = {}
|
|
35
|
+
for secret, tag in self.config.secrets.items():
|
|
36
|
+
link = f"tg://proxy?server={ip}&port={self.config.port}&secret={secret}"
|
|
37
|
+
if tag:
|
|
38
|
+
link += f"&tag={tag}"
|
|
39
|
+
links[secret[:16] + ("..." if len(secret) > 16 else "")] = link
|
|
40
|
+
return links
|
|
41
|
+
|
|
42
|
+
async def _handle_connection(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
|
|
45
43
|
conn_id = self._get_conn_id()
|
|
46
44
|
client_addr = writer.get_extra_info('peername')
|
|
47
45
|
client_ip = client_addr[0] if client_addr else "unknown"
|
|
48
46
|
|
|
49
47
|
try:
|
|
50
|
-
# Чтение handshake
|
|
51
48
|
handshake = await asyncio.wait_for(
|
|
52
49
|
reader.read(1024),
|
|
53
50
|
timeout=self.config.handshake_timeout
|
|
@@ -57,15 +54,20 @@ class MTProxyServer:
|
|
|
57
54
|
writer.close()
|
|
58
55
|
return
|
|
59
56
|
|
|
60
|
-
# Аутентификация с несколькими секретами
|
|
61
57
|
if self.config.enable_fake_tls and handshake[0] == 0x16:
|
|
62
|
-
|
|
58
|
+
from .utils import verify_fake_tls_handshake
|
|
59
|
+
success, secret = False, None
|
|
60
|
+
for secret_hex, tag in self.config.secrets.items():
|
|
61
|
+
secret_bytes = bytes.fromhex(secret_hex)
|
|
62
|
+
if verify_fake_tls_handshake(handshake, secret_bytes):
|
|
63
|
+
success = True
|
|
64
|
+
secret = secret_hex
|
|
65
|
+
break
|
|
63
66
|
if not success:
|
|
64
67
|
self.logger.warning(f"Auth failed from {client_ip}")
|
|
65
68
|
writer.close()
|
|
66
69
|
return
|
|
67
70
|
else:
|
|
68
|
-
# Классический режим - проверяем первый секрет
|
|
69
71
|
if len(handshake) < 4 or handshake[:4] != b'\xef\xef\xef\xef':
|
|
70
72
|
self.logger.warning(f"Invalid protocol from {client_ip}")
|
|
71
73
|
writer.close()
|
|
@@ -76,19 +78,14 @@ class MTProxyServer:
|
|
|
76
78
|
return
|
|
77
79
|
|
|
78
80
|
self.logger.info(f"Client connected: {client_ip} (secret: {secret[:16]}...)")
|
|
79
|
-
|
|
80
|
-
# Регистрация соединения в статистике
|
|
81
81
|
self.stats.connection_started(conn_id, client_ip, secret)
|
|
82
82
|
|
|
83
|
-
# Подключение к Telegram
|
|
84
|
-
# Используем несколько IP для балансировки
|
|
85
83
|
tg_ips = ['149.154.167.50', '149.154.167.51', '149.154.175.100']
|
|
86
84
|
tg_reader, tg_writer = await asyncio.open_connection(
|
|
87
85
|
tg_ips[hash(secret) % len(tg_ips)],
|
|
88
86
|
443
|
|
89
87
|
)
|
|
90
88
|
|
|
91
|
-
# Создание задач для пересылки данных
|
|
92
89
|
task_client = asyncio.create_task(
|
|
93
90
|
self._forward(reader, tg_writer, conn_id, sent=True)
|
|
94
91
|
)
|
|
@@ -99,7 +96,6 @@ class MTProxyServer:
|
|
|
99
96
|
self._tasks[conn_id] = task_client
|
|
100
97
|
self._tasks[f"{conn_id}_server"] = task_server
|
|
101
98
|
|
|
102
|
-
# Ожидание завершения
|
|
103
99
|
await asyncio.gather(task_client, task_server)
|
|
104
100
|
|
|
105
101
|
except asyncio.TimeoutError:
|
|
@@ -109,10 +105,8 @@ class MTProxyServer:
|
|
|
109
105
|
except Exception as e:
|
|
110
106
|
self.logger.error(f"Error handling connection from {client_ip}: {e}")
|
|
111
107
|
finally:
|
|
112
|
-
# Завершение соединения
|
|
113
108
|
self.stats.connection_ended(conn_id)
|
|
114
109
|
|
|
115
|
-
# Удаление задач
|
|
116
110
|
for task_id in [conn_id, f"{conn_id}_server"]:
|
|
117
111
|
if task_id in self._tasks:
|
|
118
112
|
self._tasks[task_id].cancel()
|
|
@@ -120,18 +114,10 @@ class MTProxyServer:
|
|
|
120
114
|
|
|
121
115
|
writer.close()
|
|
122
116
|
await writer.wait_closed()
|
|
123
|
-
|
|
124
117
|
self.logger.debug(f"Connection closed: {client_ip}")
|
|
125
118
|
|
|
126
|
-
async def _forward(
|
|
127
|
-
|
|
128
|
-
reader: asyncio.StreamReader,
|
|
129
|
-
writer: asyncio.StreamWriter,
|
|
130
|
-
conn_id: str,
|
|
131
|
-
sent: bool = False,
|
|
132
|
-
received: bool = False
|
|
133
|
-
):
|
|
134
|
-
"""Пересылка данных с обновлением статистики"""
|
|
119
|
+
async def _forward(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter,
|
|
120
|
+
conn_id: str, sent: bool = False, received: bool = False):
|
|
135
121
|
try:
|
|
136
122
|
while True:
|
|
137
123
|
data = await reader.read(8192)
|
|
@@ -141,7 +127,6 @@ class MTProxyServer:
|
|
|
141
127
|
writer.write(data)
|
|
142
128
|
await writer.drain()
|
|
143
129
|
|
|
144
|
-
# Обновление статистики
|
|
145
130
|
if sent:
|
|
146
131
|
self.stats.update_bytes(conn_id, sent=len(data))
|
|
147
132
|
if received:
|
|
@@ -155,20 +140,17 @@ class MTProxyServer:
|
|
|
155
140
|
writer.close()
|
|
156
141
|
|
|
157
142
|
async def _stats_reporter(self):
|
|
158
|
-
"""Периодический вывод статистики"""
|
|
159
143
|
while self._running:
|
|
160
144
|
await asyncio.sleep(self.config.stats_interval)
|
|
161
145
|
self.logger.info("\n" + self.stats.format_summary())
|
|
162
146
|
|
|
163
147
|
def start(self):
|
|
164
|
-
"""Запуск прокси сервера"""
|
|
165
148
|
loop = asyncio.new_event_loop()
|
|
166
149
|
asyncio.set_event_loop(loop)
|
|
167
150
|
|
|
168
151
|
self._running = True
|
|
169
152
|
|
|
170
153
|
try:
|
|
171
|
-
# Запуск сервера
|
|
172
154
|
self.server = loop.run_until_complete(
|
|
173
155
|
asyncio.start_server(
|
|
174
156
|
self._handle_connection,
|
|
@@ -177,10 +159,8 @@ class MTProxyServer:
|
|
|
177
159
|
)
|
|
178
160
|
)
|
|
179
161
|
|
|
180
|
-
# Запуск репортера статистики
|
|
181
162
|
stats_task = loop.create_task(self._stats_reporter())
|
|
182
163
|
|
|
183
|
-
# Вывод информации
|
|
184
164
|
print("=" * 60)
|
|
185
165
|
print("🚀 MTProxy Server Started")
|
|
186
166
|
print("=" * 60)
|
|
@@ -191,18 +171,14 @@ class MTProxyServer:
|
|
|
191
171
|
print("-" * 60)
|
|
192
172
|
print("Connection links:")
|
|
193
173
|
|
|
194
|
-
|
|
195
|
-
for
|
|
196
|
-
link = f"tg://proxy?server={ip}&port={self.config.port}&secret={secret}"
|
|
197
|
-
if tag:
|
|
198
|
-
link += f"&tag={tag}"
|
|
174
|
+
links = self.get_all_links()
|
|
175
|
+
for name, link in links.items():
|
|
199
176
|
print(f" {link}")
|
|
200
177
|
|
|
201
178
|
print("=" * 60)
|
|
202
179
|
print("Press Ctrl+C to stop")
|
|
203
180
|
print("=" * 60)
|
|
204
181
|
|
|
205
|
-
# Обработка сигналов
|
|
206
182
|
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
207
183
|
loop.add_signal_handler(sig, lambda: asyncio.create_task(self.stop()))
|
|
208
184
|
|
|
@@ -220,11 +196,9 @@ class MTProxyServer:
|
|
|
220
196
|
loop.close()
|
|
221
197
|
|
|
222
198
|
async def stop(self):
|
|
223
|
-
"""Остановка сервера"""
|
|
224
199
|
self.logger.info("Shutting down...")
|
|
225
200
|
self._running = False
|
|
226
201
|
|
|
227
|
-
# Отмена всех задач
|
|
228
202
|
for task in self._tasks.values():
|
|
229
203
|
task.cancel()
|
|
230
204
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Вспомогательные функции"""
|
|
2
|
+
|
|
3
|
+
import secrets
|
|
4
|
+
import hashlib
|
|
5
|
+
import hmac
|
|
6
|
+
from typing import Optional, List
|
|
7
|
+
import urllib.request
|
|
8
|
+
import socket
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def generate_secret() -> str:
|
|
12
|
+
return secrets.token_hex(16)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def verify_fake_tls_handshake(data: bytes, secret: bytes) -> bool:
|
|
16
|
+
if not data or len(data) < 51 or data[0] != 0x16:
|
|
17
|
+
return False
|
|
18
|
+
|
|
19
|
+
digest = data[11:43]
|
|
20
|
+
timestamp = data[43:51]
|
|
21
|
+
expected = hmac.new(secret, timestamp, hashlib.sha256).digest()[:32]
|
|
22
|
+
|
|
23
|
+
return hmac.compare_digest(digest, expected)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_external_ip() -> Optional[str]:
|
|
27
|
+
try:
|
|
28
|
+
with urllib.request.urlopen('https://ifconfig.me', timeout=5) as response:
|
|
29
|
+
return response.read().decode('utf-8').strip()
|
|
30
|
+
except Exception:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def find_free_port(start_port: int = 5000, end_port: int = 10000, exclude_ports: List[int] = None) -> int:
|
|
35
|
+
if exclude_ports is None:
|
|
36
|
+
exclude_ports = []
|
|
37
|
+
|
|
38
|
+
for port in range(start_port, end_port + 1):
|
|
39
|
+
if port in exclude_ports:
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
43
|
+
sock.settimeout(1)
|
|
44
|
+
result = sock.connect_ex(('127.0.0.1', port))
|
|
45
|
+
sock.close()
|
|
46
|
+
|
|
47
|
+
if result != 0:
|
|
48
|
+
return port
|
|
49
|
+
|
|
50
|
+
raise RuntimeError(f"No free port found in range {start_port}-{end_port}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_public_ip() -> Optional[str]:
|
|
54
|
+
services = [
|
|
55
|
+
'https://ifconfig.me',
|
|
56
|
+
'https://api.ipify.org',
|
|
57
|
+
'https://icanhazip.com',
|
|
58
|
+
'https://checkip.amazonaws.com'
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
for service in services:
|
|
62
|
+
try:
|
|
63
|
+
with urllib.request.urlopen(service, timeout=5) as response:
|
|
64
|
+
ip = response.read().decode('utf-8').strip()
|
|
65
|
+
if ip:
|
|
66
|
+
return ip
|
|
67
|
+
except Exception:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_local_ip() -> str:
|
|
74
|
+
try:
|
|
75
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
76
|
+
s.connect(('8.8.8.8', 80))
|
|
77
|
+
ip = s.getsockname()[0]
|
|
78
|
+
s.close()
|
|
79
|
+
return ip
|
|
80
|
+
except Exception:
|
|
81
|
+
return '127.0.0.1'
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def check_port_available(port: int, host: str = '0.0.0.0') -> bool:
|
|
85
|
+
try:
|
|
86
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
87
|
+
sock.settimeout(1)
|
|
88
|
+
result = sock.connect_ex((host, port))
|
|
89
|
+
sock.close()
|
|
90
|
+
return result != 0
|
|
91
|
+
except Exception:
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_common_ports() -> List[int]:
|
|
96
|
+
return [443, 8443, 8080, 5001, 5047, 5050, 8081, 9443, 2053, 2096, 2087, 2083]
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-mtproxy-lib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Production-ready MTProto proxy server library with multi-secret support, statistics, and daemon mode
|
|
5
5
|
Home-page: https://github.com/twosleepynights0x1/py-mtproxy-lib
|
|
6
|
-
Author:
|
|
6
|
+
Author: AneK
|
|
7
7
|
Author-email: tashova28@gmail.com
|
|
8
8
|
Project-URL: Bug Reports, https://github.com/twosleepynights0x1/py-mtproxy-lib
|
|
9
9
|
Keywords: mtproto telegram proxy vpn daemon docker
|
|
@@ -5,8 +5,8 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="py-mtproxy-lib",
|
|
8
|
-
version="0.0.
|
|
9
|
-
author="
|
|
8
|
+
version="0.0.4",
|
|
9
|
+
author="AneK",
|
|
10
10
|
author_email="tashova28@gmail.com",
|
|
11
11
|
description="Production-ready MTProto proxy server library with multi-secret support, statistics, and daemon mode",
|
|
12
12
|
long_description=long_description,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|