mirror of
https://github.com/violettoolssite/CFspider.git
synced 2026-04-05 11:29:03 +08:00
351 lines
11 KiB
Python
351 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
CFspider 本地代理服务器
|
||
支持双层代理(two_proxy),可供 v2ray 或浏览器使用
|
||
"""
|
||
|
||
import socket
|
||
import threading
|
||
import time
|
||
from urllib.parse import urlparse, quote
|
||
|
||
|
||
def generate_vless_link(
|
||
cf_proxies: str,
|
||
uuid: str,
|
||
name: str = "CFspider",
|
||
two_proxy: str = None
|
||
) -> dict:
|
||
"""
|
||
生成 VLESS 导入链接
|
||
|
||
Args:
|
||
cf_proxies: Workers 地址,如 "https://your-workers.dev"
|
||
uuid: VLESS UUID
|
||
name: 节点名称
|
||
two_proxy: 第二层代理(可选),格式 "host:port:user:pass"
|
||
|
||
Returns:
|
||
dict: {
|
||
'vless_link': str, # VLESS 链接(可导入 v2ray)
|
||
'local_proxy_cmd': str, # 启动本地代理的命令
|
||
'note': str # 使用说明
|
||
}
|
||
|
||
Example:
|
||
>>> result = cfspider.generate_vless_link(
|
||
... cf_proxies="https://your-workers.dev",
|
||
... uuid="your-uuid",
|
||
... two_proxy="us.cliproxy.io:3010:user:pass"
|
||
... )
|
||
>>> print(result['vless_link'])
|
||
"""
|
||
# 解析 Workers 地址
|
||
parsed = urlparse(cf_proxies)
|
||
if parsed.scheme:
|
||
host = parsed.netloc or parsed.path.split('/')[0]
|
||
else:
|
||
host = cf_proxies.split('/')[0]
|
||
|
||
# 生成基础 VLESS 链接(连接到 Workers)
|
||
vless_params = {
|
||
'security': 'tls',
|
||
'type': 'ws',
|
||
'host': host,
|
||
'sni': host,
|
||
'path': f'/{uuid}',
|
||
'encryption': 'none'
|
||
}
|
||
|
||
params_str = '&'.join([f'{k}={quote(str(v))}' for k, v in vless_params.items()])
|
||
vless_link = f"vless://{uuid}@{host}:443?{params_str}#{quote(name)}"
|
||
|
||
result = {
|
||
'vless_link': vless_link,
|
||
'host': host,
|
||
'uuid': uuid,
|
||
}
|
||
|
||
if two_proxy:
|
||
# 解析第二层代理
|
||
parts = two_proxy.split(':')
|
||
if len(parts) >= 2:
|
||
proxy_host = parts[0]
|
||
proxy_port = parts[1]
|
||
proxy_info = f"{proxy_host}:{proxy_port}"
|
||
if len(parts) == 4:
|
||
proxy_info += f" (user: {parts[2][:4]}***)"
|
||
else:
|
||
proxy_info = two_proxy
|
||
|
||
# 生成本地代理启动命令
|
||
result['local_proxy_cmd'] = (
|
||
f'python -m cfspider.proxy_server '
|
||
f'--cf-proxies "{cf_proxies}" '
|
||
f'--uuid "{uuid}" '
|
||
f'--two-proxy "{two_proxy}" '
|
||
f'--port 1080'
|
||
)
|
||
|
||
result['note'] = f"""
|
||
=== 双层代理使用说明 ===
|
||
|
||
由于 VLESS 协议本身不支持代理链,有两种方式使用双层代理:
|
||
|
||
【方式 1】使用 CFspider 本地代理(推荐)
|
||
运行以下命令启动本地代理服务器:
|
||
|
||
{result['local_proxy_cmd']}
|
||
|
||
然后在浏览器或系统中配置代理为:
|
||
HTTP/SOCKS5 代理: 127.0.0.1:1080
|
||
|
||
流量链路: 浏览器 → 本地代理(1080) → Workers(VLESS) → {proxy_info} → 目标网站
|
||
|
||
【方式 2】在 Workers 端配置 PROXYIP
|
||
1. 打开 Cloudflare Dashboard → Workers
|
||
2. 选择你的 Worker → Settings → Variables
|
||
3. 添加环境变量: PROXYIP = {parts[0] if len(parts) >= 2 else two_proxy}
|
||
4. 然后使用以下 VLESS 链接导入 v2ray:
|
||
|
||
{vless_link}
|
||
|
||
注意:方式 2 需要第二层代理支持无认证连接,或在 Workers 代码中配置认证。
|
||
"""
|
||
else:
|
||
result['note'] = f"""
|
||
=== VLESS 链接使用说明 ===
|
||
|
||
将以下链接导入 v2ray/clash 等客户端:
|
||
|
||
{vless_link}
|
||
|
||
流量链路: v2ray 客户端 → Workers(VLESS) → 目标网站
|
||
出口 IP: Cloudflare WARP 网络 IP
|
||
"""
|
||
|
||
return result
|
||
|
||
|
||
class TwoProxyServer:
|
||
"""
|
||
双层代理本地服务器
|
||
|
||
提供 HTTP/SOCKS5 代理接口,内部通过 VLESS + two_proxy 实现
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
cf_proxies: str,
|
||
uuid: str,
|
||
two_proxy: str = None,
|
||
host: str = '127.0.0.1',
|
||
port: int = 1080
|
||
):
|
||
"""
|
||
初始化本地代理服务器
|
||
|
||
Args:
|
||
cf_proxies: Workers 地址
|
||
uuid: VLESS UUID
|
||
two_proxy: 第二层代理,格式 "host:port:user:pass"
|
||
host: 监听地址(默认 127.0.0.1)
|
||
port: 监听端口(默认 1080)
|
||
"""
|
||
self.cf_proxies = cf_proxies
|
||
self.uuid = uuid
|
||
self.two_proxy = two_proxy
|
||
self.host = host
|
||
self.port = port
|
||
self.server = None
|
||
self.running = False
|
||
self._vless_proxy = None
|
||
self._vless_port = None
|
||
|
||
def _ensure_vless_proxy(self):
|
||
"""确保 VLESS 代理已启动"""
|
||
if self._vless_proxy is None:
|
||
from .vless_client import LocalVlessProxy
|
||
|
||
# 解析 Workers 地址
|
||
parsed = urlparse(self.cf_proxies)
|
||
if parsed.scheme:
|
||
host = parsed.netloc or parsed.path.split('/')[0]
|
||
else:
|
||
host = self.cf_proxies.split('/')[0]
|
||
|
||
vless_url = f"wss://{host}/{self.uuid}"
|
||
print(f"[INIT] Starting VLESS proxy: {vless_url}")
|
||
if self.two_proxy:
|
||
print(f"[INIT] Two-proxy enabled: {self.two_proxy.split(':')[0]}:{self.two_proxy.split(':')[1]}")
|
||
|
||
self._vless_proxy = LocalVlessProxy(vless_url, self.uuid, two_proxy=self.two_proxy)
|
||
self._vless_port = self._vless_proxy.start()
|
||
print(f"[INIT] VLESS proxy started on port {self._vless_port}")
|
||
|
||
def start(self):
|
||
"""启动代理服务器"""
|
||
self._ensure_vless_proxy()
|
||
|
||
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||
self.server.bind((self.host, self.port))
|
||
self.server.listen(50)
|
||
|
||
self.running = True
|
||
|
||
print(f"=" * 60)
|
||
print(f"CFspider Two-Proxy Server Started")
|
||
print(f"=" * 60)
|
||
print(f"Local Proxy: {self.host}:{self.port}")
|
||
print(f"Workers: {self.cf_proxies}")
|
||
if self.two_proxy:
|
||
parts = self.two_proxy.split(':')
|
||
print(f"Second Proxy: {parts[0]}:{parts[1]}")
|
||
print(f"-" * 60)
|
||
print(f"Configure your browser/system proxy to: {self.host}:{self.port}")
|
||
print(f"Press Ctrl+C to stop")
|
||
print(f"=" * 60)
|
||
|
||
try:
|
||
while self.running:
|
||
try:
|
||
self.server.settimeout(1)
|
||
client, addr = self.server.accept()
|
||
handler = threading.Thread(
|
||
target=self._handle_client,
|
||
args=(client,),
|
||
daemon=True
|
||
)
|
||
handler.start()
|
||
except socket.timeout:
|
||
continue
|
||
except KeyboardInterrupt:
|
||
print("\nShutting down...")
|
||
finally:
|
||
self.stop()
|
||
|
||
def _handle_client(self, client):
|
||
"""处理客户端连接,转发到内部 VLESS 代理"""
|
||
client_addr = None
|
||
try:
|
||
client_addr = client.getpeername()
|
||
client.settimeout(30)
|
||
|
||
# 连接到内部 VLESS 代理
|
||
vless_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
vless_sock.settimeout(30)
|
||
vless_sock.connect(('127.0.0.1', self._vless_port))
|
||
|
||
print(f"[CONN] {client_addr} -> VLESS proxy :{self._vless_port}")
|
||
|
||
# 双向转发
|
||
stop_event = threading.Event()
|
||
|
||
def forward(src, dst, name):
|
||
try:
|
||
while not stop_event.is_set():
|
||
try:
|
||
src.settimeout(1)
|
||
data = src.recv(8192)
|
||
if not data:
|
||
break
|
||
dst.sendall(data)
|
||
except socket.timeout:
|
||
continue
|
||
except Exception as e:
|
||
break
|
||
except Exception as e:
|
||
pass
|
||
finally:
|
||
stop_event.set()
|
||
|
||
t1 = threading.Thread(target=forward, args=(client, vless_sock, "C->V"), daemon=True)
|
||
t2 = threading.Thread(target=forward, args=(vless_sock, client, "V->C"), daemon=True)
|
||
t1.start()
|
||
t2.start()
|
||
|
||
# 等待任一方向结束
|
||
while not stop_event.is_set():
|
||
time.sleep(0.1)
|
||
|
||
print(f"[CLOSE] {client_addr}")
|
||
|
||
except Exception as e:
|
||
print(f"[ERROR] {client_addr}: {e}")
|
||
finally:
|
||
try:
|
||
client.close()
|
||
except:
|
||
pass
|
||
try:
|
||
vless_sock.close()
|
||
except:
|
||
pass
|
||
|
||
def stop(self):
|
||
"""停止服务器"""
|
||
self.running = False
|
||
if self.server:
|
||
try:
|
||
self.server.close()
|
||
except:
|
||
pass
|
||
if self._vless_proxy:
|
||
try:
|
||
self._vless_proxy.stop()
|
||
except:
|
||
pass
|
||
|
||
|
||
def start_proxy_server(
|
||
cf_proxies: str,
|
||
uuid: str,
|
||
two_proxy: str = None,
|
||
host: str = '127.0.0.1',
|
||
port: int = 1080
|
||
):
|
||
"""
|
||
启动本地代理服务器
|
||
|
||
Args:
|
||
cf_proxies: Workers 地址
|
||
uuid: VLESS UUID
|
||
two_proxy: 第二层代理(可选)
|
||
host: 监听地址(默认 127.0.0.1)
|
||
port: 监听端口(默认 1080)
|
||
|
||
Example:
|
||
>>> cfspider.start_proxy_server(
|
||
... cf_proxies="https://your-workers.dev",
|
||
... uuid="your-uuid",
|
||
... two_proxy="us.cliproxy.io:3010:user:pass",
|
||
... port=1080
|
||
... )
|
||
"""
|
||
server = TwoProxyServer(cf_proxies, uuid, two_proxy, host, port)
|
||
server.start()
|
||
|
||
|
||
# 命令行入口
|
||
if __name__ == '__main__':
|
||
import argparse
|
||
|
||
parser = argparse.ArgumentParser(description='CFspider Two-Proxy Server')
|
||
parser.add_argument('--cf-proxies', required=True, help='Workers address')
|
||
parser.add_argument('--uuid', required=True, help='VLESS UUID')
|
||
parser.add_argument('--two-proxy', help='Second layer proxy (host:port:user:pass)')
|
||
parser.add_argument('--host', default='127.0.0.1', help='Listen host (default: 127.0.0.1)')
|
||
parser.add_argument('--port', type=int, default=1080, help='Listen port (default: 1080)')
|
||
|
||
args = parser.parse_args()
|
||
|
||
start_proxy_server(
|
||
cf_proxies=args.cf_proxies,
|
||
uuid=args.uuid,
|
||
two_proxy=args.two_proxy,
|
||
host=args.host,
|
||
port=args.port
|
||
)
|
||
|