cfspider 1.7.4__py3-none-any.whl
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.
- cfspider/__init__.py +230 -0
- cfspider/api.py +937 -0
- cfspider/async_api.py +418 -0
- cfspider/async_session.py +281 -0
- cfspider/browser.py +335 -0
- cfspider/cli.py +81 -0
- cfspider/impersonate.py +388 -0
- cfspider/ip_map.py +522 -0
- cfspider/mirror.py +682 -0
- cfspider/session.py +239 -0
- cfspider/stealth.py +537 -0
- cfspider/vless_client.py +572 -0
- cfspider-1.7.4.dist-info/METADATA +1390 -0
- cfspider-1.7.4.dist-info/RECORD +18 -0
- cfspider-1.7.4.dist-info/WHEEL +5 -0
- cfspider-1.7.4.dist-info/entry_points.txt +2 -0
- cfspider-1.7.4.dist-info/licenses/LICENSE +201 -0
- cfspider-1.7.4.dist-info/top_level.txt +1 -0
cfspider/vless_client.py
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CFspider 内置 VLESS 客户端
|
|
3
|
+
通过 WebSocket 连接 edgetunnel,提供本地 HTTP 代理
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import socket
|
|
7
|
+
import struct
|
|
8
|
+
import threading
|
|
9
|
+
import ssl
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
from urllib.parse import urlparse
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class VlessClient:
|
|
16
|
+
"""VLESS 协议客户端"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, ws_url, vless_uuid=None):
|
|
19
|
+
"""
|
|
20
|
+
初始化 VLESS 客户端
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
ws_url: edgetunnel WebSocket 地址,如 "wss://v2.kami666.xyz"
|
|
24
|
+
vless_uuid: VLESS UUID,如不提供则自动生成
|
|
25
|
+
"""
|
|
26
|
+
self.ws_url = ws_url
|
|
27
|
+
self.vless_uuid = vless_uuid or str(uuid.uuid4())
|
|
28
|
+
|
|
29
|
+
parsed = urlparse(ws_url)
|
|
30
|
+
self.host = parsed.hostname
|
|
31
|
+
self.port = parsed.port or (443 if parsed.scheme == 'wss' else 80)
|
|
32
|
+
self.path = parsed.path or '/'
|
|
33
|
+
self.use_ssl = parsed.scheme == 'wss'
|
|
34
|
+
|
|
35
|
+
def _create_vless_header(self, target_host, target_port):
|
|
36
|
+
"""创建 VLESS 请求头"""
|
|
37
|
+
# VLESS 协议版本
|
|
38
|
+
header = bytes([0])
|
|
39
|
+
|
|
40
|
+
# UUID (16 bytes)
|
|
41
|
+
uuid_bytes = uuid.UUID(self.vless_uuid).bytes
|
|
42
|
+
header += uuid_bytes
|
|
43
|
+
|
|
44
|
+
# 附加信息长度
|
|
45
|
+
header += bytes([0])
|
|
46
|
+
|
|
47
|
+
# 命令 (1 = TCP)
|
|
48
|
+
header += bytes([1])
|
|
49
|
+
|
|
50
|
+
# 目标端口
|
|
51
|
+
header += struct.pack('>H', target_port)
|
|
52
|
+
|
|
53
|
+
# 地址类型和地址
|
|
54
|
+
try:
|
|
55
|
+
# 尝试解析为 IPv4
|
|
56
|
+
socket.inet_aton(target_host)
|
|
57
|
+
header += bytes([1]) # IPv4
|
|
58
|
+
header += socket.inet_aton(target_host)
|
|
59
|
+
except socket.error:
|
|
60
|
+
try:
|
|
61
|
+
# 尝试解析为 IPv6
|
|
62
|
+
socket.inet_pton(socket.AF_INET6, target_host)
|
|
63
|
+
header += bytes([3]) # IPv6
|
|
64
|
+
header += socket.inet_pton(socket.AF_INET6, target_host)
|
|
65
|
+
except socket.error:
|
|
66
|
+
# 域名
|
|
67
|
+
header += bytes([2]) # 域名
|
|
68
|
+
domain_bytes = target_host.encode('utf-8')
|
|
69
|
+
header += bytes([len(domain_bytes)])
|
|
70
|
+
header += domain_bytes
|
|
71
|
+
|
|
72
|
+
return header
|
|
73
|
+
|
|
74
|
+
def _websocket_handshake(self, sock):
|
|
75
|
+
"""执行 WebSocket 握手"""
|
|
76
|
+
import base64
|
|
77
|
+
import hashlib
|
|
78
|
+
import os
|
|
79
|
+
|
|
80
|
+
# 生成随机 key
|
|
81
|
+
key = base64.b64encode(os.urandom(16)).decode('utf-8')
|
|
82
|
+
|
|
83
|
+
# 构建握手请求
|
|
84
|
+
request = (
|
|
85
|
+
f"GET {self.path} HTTP/1.1\r\n"
|
|
86
|
+
f"Host: {self.host}\r\n"
|
|
87
|
+
f"Upgrade: websocket\r\n"
|
|
88
|
+
f"Connection: Upgrade\r\n"
|
|
89
|
+
f"Sec-WebSocket-Key: {key}\r\n"
|
|
90
|
+
f"Sec-WebSocket-Version: 13\r\n"
|
|
91
|
+
f"\r\n"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
sock.sendall(request.encode('utf-8'))
|
|
95
|
+
|
|
96
|
+
# 读取响应
|
|
97
|
+
response = b''
|
|
98
|
+
while b'\r\n\r\n' not in response:
|
|
99
|
+
chunk = sock.recv(1024)
|
|
100
|
+
if not chunk:
|
|
101
|
+
raise Exception("WebSocket 握手失败")
|
|
102
|
+
response += chunk
|
|
103
|
+
|
|
104
|
+
if b'101' not in response:
|
|
105
|
+
raise Exception(f"WebSocket 握手失败: {response.decode('utf-8', errors='ignore')}")
|
|
106
|
+
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
def _send_ws_frame(self, sock, data):
|
|
110
|
+
"""发送 WebSocket 帧"""
|
|
111
|
+
import os
|
|
112
|
+
|
|
113
|
+
# 构建帧头
|
|
114
|
+
frame = bytes([0x82]) # Binary frame, FIN=1
|
|
115
|
+
|
|
116
|
+
length = len(data)
|
|
117
|
+
if length <= 125:
|
|
118
|
+
frame += bytes([0x80 | length]) # Masked
|
|
119
|
+
elif length <= 65535:
|
|
120
|
+
frame += bytes([0x80 | 126])
|
|
121
|
+
frame += struct.pack('>H', length)
|
|
122
|
+
else:
|
|
123
|
+
frame += bytes([0x80 | 127])
|
|
124
|
+
frame += struct.pack('>Q', length)
|
|
125
|
+
|
|
126
|
+
# 掩码
|
|
127
|
+
mask = os.urandom(4)
|
|
128
|
+
frame += mask
|
|
129
|
+
|
|
130
|
+
# 掩码数据
|
|
131
|
+
masked_data = bytes([data[i] ^ mask[i % 4] for i in range(len(data))])
|
|
132
|
+
frame += masked_data
|
|
133
|
+
|
|
134
|
+
sock.sendall(frame)
|
|
135
|
+
|
|
136
|
+
def _recv_ws_frame(self, sock):
|
|
137
|
+
"""接收 WebSocket 帧"""
|
|
138
|
+
# 读取帧头
|
|
139
|
+
header = sock.recv(2)
|
|
140
|
+
if len(header) < 2:
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
opcode = header[0] & 0x0F
|
|
144
|
+
masked = (header[1] & 0x80) != 0
|
|
145
|
+
length = header[1] & 0x7F
|
|
146
|
+
|
|
147
|
+
if length == 126:
|
|
148
|
+
length_bytes = sock.recv(2)
|
|
149
|
+
length = struct.unpack('>H', length_bytes)[0]
|
|
150
|
+
elif length == 127:
|
|
151
|
+
length_bytes = sock.recv(8)
|
|
152
|
+
length = struct.unpack('>Q', length_bytes)[0]
|
|
153
|
+
|
|
154
|
+
if masked:
|
|
155
|
+
mask = sock.recv(4)
|
|
156
|
+
|
|
157
|
+
# 读取数据
|
|
158
|
+
data = b''
|
|
159
|
+
while len(data) < length:
|
|
160
|
+
chunk = sock.recv(length - len(data))
|
|
161
|
+
if not chunk:
|
|
162
|
+
break
|
|
163
|
+
data += chunk
|
|
164
|
+
|
|
165
|
+
if masked:
|
|
166
|
+
data = bytes([data[i] ^ mask[i % 4] for i in range(len(data))])
|
|
167
|
+
|
|
168
|
+
# 处理关闭帧
|
|
169
|
+
if opcode == 0x08:
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
return data
|
|
173
|
+
|
|
174
|
+
def connect(self, target_host, target_port):
|
|
175
|
+
"""
|
|
176
|
+
通过 VLESS 连接到目标
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
VlessConnection: 可用于读写的连接对象
|
|
180
|
+
"""
|
|
181
|
+
# 创建连接
|
|
182
|
+
sock = socket.create_connection((self.host, self.port), timeout=30)
|
|
183
|
+
|
|
184
|
+
if self.use_ssl:
|
|
185
|
+
context = ssl.create_default_context()
|
|
186
|
+
sock = context.wrap_socket(sock, server_hostname=self.host)
|
|
187
|
+
|
|
188
|
+
# WebSocket 握手
|
|
189
|
+
self._websocket_handshake(sock)
|
|
190
|
+
|
|
191
|
+
# 创建 VLESS 头(稍后与第一个数据包一起发送)
|
|
192
|
+
vless_header = self._create_vless_header(target_host, target_port)
|
|
193
|
+
|
|
194
|
+
return VlessConnection(sock, self, vless_header)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class VlessConnection:
|
|
198
|
+
"""VLESS 连接封装"""
|
|
199
|
+
|
|
200
|
+
def __init__(self, sock, client, vless_header=None):
|
|
201
|
+
self.sock = sock
|
|
202
|
+
self.client = client
|
|
203
|
+
self.buffer = b''
|
|
204
|
+
self.first_response = True
|
|
205
|
+
self.vless_header = vless_header # 第一次发送时需要带上
|
|
206
|
+
self.first_send = True
|
|
207
|
+
|
|
208
|
+
def send(self, data):
|
|
209
|
+
"""发送数据"""
|
|
210
|
+
if self.first_send and self.vless_header:
|
|
211
|
+
# 第一次发送时,将 VLESS 头和数据一起发送
|
|
212
|
+
self.client._send_ws_frame(self.sock, self.vless_header + data)
|
|
213
|
+
self.first_send = False
|
|
214
|
+
else:
|
|
215
|
+
self.client._send_ws_frame(self.sock, data)
|
|
216
|
+
|
|
217
|
+
def recv(self, size):
|
|
218
|
+
"""接收数据"""
|
|
219
|
+
# 如果缓冲区不够,尝试接收更多数据
|
|
220
|
+
if len(self.buffer) < size:
|
|
221
|
+
try:
|
|
222
|
+
frame = self.client._recv_ws_frame(self.sock)
|
|
223
|
+
if frame:
|
|
224
|
+
# 第一个响应需要跳过 VLESS 响应头
|
|
225
|
+
if self.first_response and len(frame) >= 2:
|
|
226
|
+
addon_len = frame[1] if len(frame) > 1 else 0
|
|
227
|
+
frame = frame[2 + addon_len:]
|
|
228
|
+
self.first_response = False
|
|
229
|
+
self.buffer += frame
|
|
230
|
+
except:
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
result = self.buffer[:size]
|
|
234
|
+
self.buffer = self.buffer[size:]
|
|
235
|
+
return result
|
|
236
|
+
|
|
237
|
+
def recv_all(self):
|
|
238
|
+
"""接收所有可用数据"""
|
|
239
|
+
try:
|
|
240
|
+
self.sock.setblocking(False)
|
|
241
|
+
while True:
|
|
242
|
+
try:
|
|
243
|
+
frame = self.client._recv_ws_frame(self.sock)
|
|
244
|
+
if frame is None:
|
|
245
|
+
break
|
|
246
|
+
|
|
247
|
+
if self.first_response and len(frame) >= 2:
|
|
248
|
+
addon_len = frame[1] if len(frame) > 1 else 0
|
|
249
|
+
frame = frame[2 + addon_len:]
|
|
250
|
+
self.first_response = False
|
|
251
|
+
|
|
252
|
+
self.buffer += frame
|
|
253
|
+
except (BlockingIOError, ssl.SSLWantReadError):
|
|
254
|
+
break
|
|
255
|
+
finally:
|
|
256
|
+
self.sock.setblocking(True)
|
|
257
|
+
|
|
258
|
+
result = self.buffer
|
|
259
|
+
self.buffer = b''
|
|
260
|
+
return result
|
|
261
|
+
|
|
262
|
+
def close(self):
|
|
263
|
+
"""关闭连接"""
|
|
264
|
+
try:
|
|
265
|
+
self.sock.close()
|
|
266
|
+
except:
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class LocalVlessProxy:
|
|
271
|
+
"""本地 VLESS HTTP 代理服务器"""
|
|
272
|
+
|
|
273
|
+
def __init__(self, ws_url, vless_uuid=None):
|
|
274
|
+
"""
|
|
275
|
+
初始化本地代理
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
ws_url: edgetunnel WebSocket 地址
|
|
279
|
+
vless_uuid: VLESS UUID
|
|
280
|
+
"""
|
|
281
|
+
self.ws_url = ws_url
|
|
282
|
+
self.vless_uuid = vless_uuid
|
|
283
|
+
self.server = None
|
|
284
|
+
self.thread = None
|
|
285
|
+
self.port = None
|
|
286
|
+
self.running = False
|
|
287
|
+
|
|
288
|
+
def start(self):
|
|
289
|
+
"""启动代理服务器"""
|
|
290
|
+
# 找可用端口
|
|
291
|
+
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
292
|
+
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
293
|
+
self.server.bind(('127.0.0.1', 0))
|
|
294
|
+
self.port = self.server.getsockname()[1]
|
|
295
|
+
self.server.listen(10)
|
|
296
|
+
|
|
297
|
+
self.running = True
|
|
298
|
+
self.thread = threading.Thread(target=self._serve, daemon=True)
|
|
299
|
+
self.thread.start()
|
|
300
|
+
|
|
301
|
+
# 等待服务器就绪
|
|
302
|
+
time.sleep(0.1)
|
|
303
|
+
return self.port
|
|
304
|
+
|
|
305
|
+
def _serve(self):
|
|
306
|
+
"""服务循环"""
|
|
307
|
+
self.server.settimeout(1)
|
|
308
|
+
while self.running:
|
|
309
|
+
try:
|
|
310
|
+
client, addr = self.server.accept()
|
|
311
|
+
handler = threading.Thread(
|
|
312
|
+
target=self._handle_client,
|
|
313
|
+
args=(client,),
|
|
314
|
+
daemon=True
|
|
315
|
+
)
|
|
316
|
+
handler.start()
|
|
317
|
+
except socket.timeout:
|
|
318
|
+
continue
|
|
319
|
+
except:
|
|
320
|
+
break
|
|
321
|
+
|
|
322
|
+
def _handle_client(self, client):
|
|
323
|
+
"""处理客户端连接"""
|
|
324
|
+
try:
|
|
325
|
+
client.settimeout(30)
|
|
326
|
+
|
|
327
|
+
# 读取请求
|
|
328
|
+
request = b''
|
|
329
|
+
while b'\r\n\r\n' not in request:
|
|
330
|
+
chunk = client.recv(4096)
|
|
331
|
+
if not chunk:
|
|
332
|
+
return
|
|
333
|
+
request += chunk
|
|
334
|
+
|
|
335
|
+
# 解析请求
|
|
336
|
+
lines = request.split(b'\r\n')
|
|
337
|
+
first_line = lines[0].decode('utf-8')
|
|
338
|
+
parts = first_line.split(' ')
|
|
339
|
+
|
|
340
|
+
if len(parts) < 2:
|
|
341
|
+
return
|
|
342
|
+
|
|
343
|
+
method = parts[0]
|
|
344
|
+
|
|
345
|
+
if method == 'CONNECT':
|
|
346
|
+
# HTTPS 代理
|
|
347
|
+
target = parts[1]
|
|
348
|
+
if ':' in target:
|
|
349
|
+
host, port = target.rsplit(':', 1)
|
|
350
|
+
port = int(port)
|
|
351
|
+
else:
|
|
352
|
+
host = target
|
|
353
|
+
port = 443
|
|
354
|
+
|
|
355
|
+
self._handle_connect(client, host, port)
|
|
356
|
+
else:
|
|
357
|
+
# HTTP 代理
|
|
358
|
+
url = parts[1]
|
|
359
|
+
self._handle_http(client, method, url, request)
|
|
360
|
+
|
|
361
|
+
except Exception as e:
|
|
362
|
+
pass
|
|
363
|
+
finally:
|
|
364
|
+
try:
|
|
365
|
+
client.close()
|
|
366
|
+
except:
|
|
367
|
+
pass
|
|
368
|
+
|
|
369
|
+
def _handle_connect(self, client, host, port):
|
|
370
|
+
"""处理 HTTPS CONNECT 请求"""
|
|
371
|
+
try:
|
|
372
|
+
# 连接到 VLESS
|
|
373
|
+
vless = VlessClient(self.ws_url, self.vless_uuid)
|
|
374
|
+
conn = vless.connect(host, port)
|
|
375
|
+
|
|
376
|
+
# 发送连接成功
|
|
377
|
+
client.sendall(b'HTTP/1.1 200 Connection Established\r\n\r\n')
|
|
378
|
+
|
|
379
|
+
# 双向转发(使用线程)
|
|
380
|
+
self._relay_bidirectional(client, conn)
|
|
381
|
+
|
|
382
|
+
except Exception as e:
|
|
383
|
+
try:
|
|
384
|
+
client.sendall(b'HTTP/1.1 502 Bad Gateway\r\n\r\n')
|
|
385
|
+
except:
|
|
386
|
+
pass
|
|
387
|
+
|
|
388
|
+
def _handle_http(self, client, method, url, original_request):
|
|
389
|
+
"""处理 HTTP 请求"""
|
|
390
|
+
try:
|
|
391
|
+
parsed = urlparse(url)
|
|
392
|
+
host = parsed.hostname
|
|
393
|
+
port = parsed.port or 80
|
|
394
|
+
path = parsed.path or '/'
|
|
395
|
+
if parsed.query:
|
|
396
|
+
path += '?' + parsed.query
|
|
397
|
+
|
|
398
|
+
# 连接到 VLESS
|
|
399
|
+
vless = VlessClient(self.ws_url, self.vless_uuid)
|
|
400
|
+
conn = vless.connect(host, port)
|
|
401
|
+
|
|
402
|
+
# 重建请求
|
|
403
|
+
lines = original_request.split(b'\r\n')
|
|
404
|
+
lines[0] = f'{method} {path} HTTP/1.1'.encode('utf-8')
|
|
405
|
+
|
|
406
|
+
# 更新 Host 头
|
|
407
|
+
new_lines = [lines[0]]
|
|
408
|
+
has_host = False
|
|
409
|
+
for line in lines[1:]:
|
|
410
|
+
if line.lower().startswith(b'host:'):
|
|
411
|
+
new_lines.append(f'Host: {host}'.encode('utf-8'))
|
|
412
|
+
has_host = True
|
|
413
|
+
elif line.lower().startswith(b'proxy-'):
|
|
414
|
+
continue
|
|
415
|
+
else:
|
|
416
|
+
new_lines.append(line)
|
|
417
|
+
|
|
418
|
+
if not has_host:
|
|
419
|
+
new_lines.insert(1, f'Host: {host}'.encode('utf-8'))
|
|
420
|
+
|
|
421
|
+
request = b'\r\n'.join(new_lines)
|
|
422
|
+
conn.send(request)
|
|
423
|
+
|
|
424
|
+
# 读取响应并转发
|
|
425
|
+
self._relay_response(client, conn)
|
|
426
|
+
|
|
427
|
+
except Exception as e:
|
|
428
|
+
client.sendall(b'HTTP/1.1 502 Bad Gateway\r\n\r\n')
|
|
429
|
+
|
|
430
|
+
def _relay_bidirectional(self, client, conn):
|
|
431
|
+
"""双向数据转发(使用线程)"""
|
|
432
|
+
import threading
|
|
433
|
+
|
|
434
|
+
stop_event = threading.Event()
|
|
435
|
+
|
|
436
|
+
def client_to_vless():
|
|
437
|
+
try:
|
|
438
|
+
while not stop_event.is_set():
|
|
439
|
+
try:
|
|
440
|
+
client.settimeout(1)
|
|
441
|
+
data = client.recv(8192)
|
|
442
|
+
if data:
|
|
443
|
+
conn.send(data)
|
|
444
|
+
else:
|
|
445
|
+
break
|
|
446
|
+
except socket.timeout:
|
|
447
|
+
continue
|
|
448
|
+
except:
|
|
449
|
+
break
|
|
450
|
+
finally:
|
|
451
|
+
stop_event.set()
|
|
452
|
+
|
|
453
|
+
def vless_to_client():
|
|
454
|
+
try:
|
|
455
|
+
while not stop_event.is_set():
|
|
456
|
+
try:
|
|
457
|
+
conn.sock.settimeout(1)
|
|
458
|
+
frame = self._recv_ws_frame_safe(conn)
|
|
459
|
+
if frame:
|
|
460
|
+
client.sendall(frame)
|
|
461
|
+
elif frame is None:
|
|
462
|
+
break
|
|
463
|
+
except socket.timeout:
|
|
464
|
+
continue
|
|
465
|
+
except:
|
|
466
|
+
break
|
|
467
|
+
finally:
|
|
468
|
+
stop_event.set()
|
|
469
|
+
|
|
470
|
+
t1 = threading.Thread(target=client_to_vless, daemon=True)
|
|
471
|
+
t2 = threading.Thread(target=vless_to_client, daemon=True)
|
|
472
|
+
t1.start()
|
|
473
|
+
t2.start()
|
|
474
|
+
|
|
475
|
+
# 等待任一方向结束
|
|
476
|
+
while not stop_event.is_set():
|
|
477
|
+
time.sleep(0.1)
|
|
478
|
+
|
|
479
|
+
conn.close()
|
|
480
|
+
|
|
481
|
+
def _recv_ws_frame_safe(self, conn):
|
|
482
|
+
"""安全地接收 WebSocket 帧"""
|
|
483
|
+
try:
|
|
484
|
+
sock = conn.sock
|
|
485
|
+
header = sock.recv(2)
|
|
486
|
+
if len(header) < 2:
|
|
487
|
+
return None
|
|
488
|
+
|
|
489
|
+
opcode = header[0] & 0x0F
|
|
490
|
+
masked = (header[1] & 0x80) != 0
|
|
491
|
+
length = header[1] & 0x7F
|
|
492
|
+
|
|
493
|
+
if length == 126:
|
|
494
|
+
length_bytes = sock.recv(2)
|
|
495
|
+
length = struct.unpack('>H', length_bytes)[0]
|
|
496
|
+
elif length == 127:
|
|
497
|
+
length_bytes = sock.recv(8)
|
|
498
|
+
length = struct.unpack('>Q', length_bytes)[0]
|
|
499
|
+
|
|
500
|
+
if masked:
|
|
501
|
+
mask = sock.recv(4)
|
|
502
|
+
|
|
503
|
+
data = b''
|
|
504
|
+
while len(data) < length:
|
|
505
|
+
chunk = sock.recv(min(length - len(data), 8192))
|
|
506
|
+
if not chunk:
|
|
507
|
+
break
|
|
508
|
+
data += chunk
|
|
509
|
+
|
|
510
|
+
if masked:
|
|
511
|
+
data = bytes([data[i] ^ mask[i % 4] for i in range(len(data))])
|
|
512
|
+
|
|
513
|
+
if opcode == 0x08:
|
|
514
|
+
return None
|
|
515
|
+
|
|
516
|
+
# 跳过 VLESS 响应头
|
|
517
|
+
if conn.first_response and len(data) >= 2:
|
|
518
|
+
addon_len = data[1]
|
|
519
|
+
data = data[2 + addon_len:]
|
|
520
|
+
conn.first_response = False
|
|
521
|
+
|
|
522
|
+
return data
|
|
523
|
+
except:
|
|
524
|
+
return b''
|
|
525
|
+
|
|
526
|
+
def _relay_response(self, client, conn):
|
|
527
|
+
"""转发 HTTP 响应"""
|
|
528
|
+
try:
|
|
529
|
+
# 读取响应
|
|
530
|
+
response = b''
|
|
531
|
+
while True:
|
|
532
|
+
data = conn.recv(8192)
|
|
533
|
+
if not data:
|
|
534
|
+
break
|
|
535
|
+
response += data
|
|
536
|
+
client.sendall(data)
|
|
537
|
+
|
|
538
|
+
# 检查是否完成
|
|
539
|
+
if b'\r\n\r\n' in response:
|
|
540
|
+
# 简单处理:继续读取直到没有数据
|
|
541
|
+
conn.sock.settimeout(0.5)
|
|
542
|
+
try:
|
|
543
|
+
while True:
|
|
544
|
+
data = conn.recv(8192)
|
|
545
|
+
if not data:
|
|
546
|
+
break
|
|
547
|
+
client.sendall(data)
|
|
548
|
+
except socket.timeout:
|
|
549
|
+
pass
|
|
550
|
+
break
|
|
551
|
+
finally:
|
|
552
|
+
conn.close()
|
|
553
|
+
|
|
554
|
+
def stop(self):
|
|
555
|
+
"""停止代理服务器"""
|
|
556
|
+
self.running = False
|
|
557
|
+
if self.server:
|
|
558
|
+
try:
|
|
559
|
+
self.server.close()
|
|
560
|
+
except:
|
|
561
|
+
pass
|
|
562
|
+
self.server = None
|
|
563
|
+
self.thread = None
|
|
564
|
+
self.port = None
|
|
565
|
+
|
|
566
|
+
@property
|
|
567
|
+
def proxy_url(self):
|
|
568
|
+
"""获取代理 URL"""
|
|
569
|
+
if self.port:
|
|
570
|
+
return f"http://127.0.0.1:{self.port}"
|
|
571
|
+
return None
|
|
572
|
+
|