|
|
|
#!/usr/bin/python
|
|
|
|
# -*- coding:utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
|
|
|
# ShadowsocksR构建器
|
|
|
|
|
|
|
|
## 节点格式
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"server": "...",
|
|
|
|
"port": ...,
|
|
|
|
"method": "...",
|
|
|
|
"passwd": "...",
|
|
|
|
"protocol": "...",
|
|
|
|
"protocolParam": "...",
|
|
|
|
"obfs": "...",
|
|
|
|
"obfsParam": "..."
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
+ server (str) -> 必选, 服务器地址 (IPv4 / IPv6 / Domain)
|
|
|
|
|
|
|
|
+ port (int) -> 必选, 服务器端口 (1 ~ 65535)
|
|
|
|
|
|
|
|
+ method (str) -> 必选, ShadowsocksR加密方式 (`aes-128-cfb`,`aes-192-cfb`,`aes-256-cfb`,
|
|
|
|
`aes-128-cfb1`,`aes-192-cfb1`,`aes-256-cfb1`,
|
|
|
|
`aes-128-cfb8`,`aes-192-cfb8`,`aes-256-cfb8`,
|
|
|
|
`aes-128-ctr`,`aes-192-ctr`,`aes-256-ctr`,
|
|
|
|
`aes-128-gcm`,`aes-192-gcm`,`aes-256-gcm`,
|
|
|
|
`aes-128-ofb`,`aes-192-ofb`,`aes-256-ofb`,
|
|
|
|
`camellia-128-cfb`,`camellia-192-cfb`,`camellia-256-cfb`,
|
|
|
|
`none`,`table`,`rc4`,`rc4-md5`,`rc4-md5-6`,`bf-cfb`,
|
|
|
|
`cast5-cfb`,`des-cfb`,`idea-cfb`,`seed-cfb`,`rc2-cfb`,
|
|
|
|
`salsa20`,`xsalsa20`,`chacha20`,`xchacha20`,`chacha20-ietf`)
|
|
|
|
|
|
|
|
+ passwd (str) -> 必选, ShadowsocksR连接密码
|
|
|
|
|
|
|
|
+ protocol (str) -> 必选, ShadowsocksR连接协议 (`origin`,`verify_sha1`,`verify_simple`,`verify_deflate`,
|
|
|
|
`auth_simple`,`auth_sha1`,`auth_sha1_v2`,`auth_sha1_v4`,
|
|
|
|
`auth_aes128`,`auth_aes128_md5`,`auth_aes128_sha1`,
|
|
|
|
`auth_chain_a`,`auth_chain_b`,`auth_chain_c`,
|
|
|
|
`auth_chain_d`,`auth_chain_e`,`auth_chain_f`)
|
|
|
|
|
|
|
|
+ protocolParam (str) -> 必选, ShadowsocksR协议参数
|
|
|
|
|
|
|
|
+ obfs (str) -> 必选, ShadowsocksR混淆方式 (`plain`,`http_post`,`http_simple`,`tls_simple`,
|
|
|
|
`tls1.2_ticket_auth`,`tls1.2_ticket_fastauth`,`random_head`)
|
|
|
|
|
|
|
|
+ obfsParam (str) -> 必选, ShadowsocksR混淆参数
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
ssrMethodList = [ # ShadowsocksR加密方式
|
|
|
|
"aes-128-cfb",
|
|
|
|
"aes-192-cfb",
|
|
|
|
"aes-256-cfb",
|
|
|
|
"aes-128-cfb1",
|
|
|
|
"aes-192-cfb1",
|
|
|
|
"aes-256-cfb1",
|
|
|
|
"aes-128-cfb8",
|
|
|
|
"aes-192-cfb8",
|
|
|
|
"aes-256-cfb8",
|
|
|
|
"aes-128-ctr",
|
|
|
|
"aes-192-ctr",
|
|
|
|
"aes-256-ctr",
|
|
|
|
"aes-128-gcm",
|
|
|
|
"aes-192-gcm",
|
|
|
|
"aes-256-gcm",
|
|
|
|
"aes-128-ofb",
|
|
|
|
"aes-192-ofb",
|
|
|
|
"aes-256-ofb",
|
|
|
|
"camellia-128-cfb",
|
|
|
|
"camellia-192-cfb",
|
|
|
|
"camellia-256-cfb",
|
|
|
|
"none",
|
|
|
|
"table",
|
|
|
|
"rc4",
|
|
|
|
"rc4-md5",
|
|
|
|
"rc4-md5-6",
|
|
|
|
"bf-cfb",
|
|
|
|
"cast5-cfb",
|
|
|
|
"des-cfb",
|
|
|
|
"idea-cfb",
|
|
|
|
"seed-cfb",
|
|
|
|
"rc2-cfb",
|
|
|
|
"salsa20",
|
|
|
|
"xsalsa20",
|
|
|
|
"chacha20",
|
|
|
|
"xchacha20",
|
|
|
|
"chacha20-ietf",
|
|
|
|
]
|
|
|
|
|
|
|
|
ssrProtocolList = [ # ShadowsocksR协议列表
|
|
|
|
"origin",
|
|
|
|
"verify_sha1",
|
|
|
|
"verify_simple",
|
|
|
|
"verify_deflate",
|
|
|
|
"auth_simple",
|
|
|
|
"auth_sha1",
|
|
|
|
"auth_sha1_v2",
|
|
|
|
"auth_sha1_v4",
|
|
|
|
"auth_aes128",
|
|
|
|
"auth_aes128_md5",
|
|
|
|
"auth_aes128_sha1",
|
|
|
|
"auth_chain_a",
|
|
|
|
"auth_chain_b",
|
|
|
|
"auth_chain_c",
|
|
|
|
"auth_chain_d",
|
|
|
|
"auth_chain_e",
|
|
|
|
"auth_chain_f",
|
|
|
|
]
|
|
|
|
|
|
|
|
ssrObfsList = [ # ShadowsocksR混淆方式
|
|
|
|
"plain",
|
|
|
|
"http_post",
|
|
|
|
"http_simple",
|
|
|
|
"tls_simple",
|
|
|
|
"tls1.2_ticket_auth",
|
|
|
|
"tls1.2_ticket_fastauth",
|
|
|
|
"random_head",
|
|
|
|
]
|
|
|
|
|
|
|
|
def __ssrFormatCheck(proxyInfo: dict) -> bool: # ShadowsocksR参数检查
|
|
|
|
if 'server' not in proxyInfo or not isinstance(proxyInfo['server'], str): # server -> str
|
|
|
|
return False
|
|
|
|
if 'port' not in proxyInfo or not isinstance(proxyInfo['port'], int): # port -> int
|
|
|
|
return False
|
|
|
|
if 'method' not in proxyInfo or not isinstance(proxyInfo['method'], str): # method -> str
|
|
|
|
return False
|
|
|
|
if 'passwd' not in proxyInfo or not isinstance(proxyInfo['passwd'], str): # passwd -> str
|
|
|
|
return False
|
|
|
|
if 'protocol' not in proxyInfo or not isinstance(proxyInfo['protocol'], str): # protocol -> str
|
|
|
|
return False
|
|
|
|
if 'protocolParam' not in proxyInfo or not isinstance(proxyInfo['protocolParam'], str): # protocolParam -> str
|
|
|
|
return False
|
|
|
|
if 'obfs' not in proxyInfo or not isinstance(proxyInfo['obfs'], str): # obfs -> str
|
|
|
|
return False
|
|
|
|
if 'obfsParam' not in proxyInfo or not isinstance(proxyInfo['obfsParam'], str): # obfsParam -> str
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def load(proxyInfo: dict, socksPort: int, configFile: str) -> tuple[list or None, str or None, dict or None]:
|
|
|
|
"""
|
|
|
|
ShadowsocksR配置载入
|
|
|
|
proxyInfo: 节点信息
|
|
|
|
socksPort: 本地通讯端口
|
|
|
|
configFile: 配置文件路径
|
|
|
|
|
|
|
|
节点有误:
|
|
|
|
return None, None, None
|
|
|
|
|
|
|
|
载入成功:
|
|
|
|
return startCommand, fileContent, envVar
|
|
|
|
"""
|
|
|
|
if not __ssrFormatCheck(proxyInfo): # 参数有误
|
|
|
|
return None, None, None
|
|
|
|
if not proxyInfo['method'] in ssrMethodList: # 匹配不到加密方法
|
|
|
|
return None, None, None
|
|
|
|
if not proxyInfo['protocol'] in ssrProtocolList: # 匹配不到协议
|
|
|
|
return None, None, None
|
|
|
|
if not proxyInfo['obfs'] in ssrObfsList: # 匹配不到混淆方式
|
|
|
|
return None, None, None
|
|
|
|
config = {
|
|
|
|
'server': proxyInfo['server'],
|
|
|
|
'server_port': proxyInfo['port'],
|
|
|
|
'local_address': '127.0.0.1',
|
|
|
|
'local_port': socksPort,
|
|
|
|
'password': proxyInfo['passwd'],
|
|
|
|
'method': proxyInfo['method'],
|
|
|
|
'protocol': proxyInfo['protocol'],
|
|
|
|
'protocol_param': proxyInfo['protocolParam'],
|
|
|
|
'obfs': proxyInfo['obfs'],
|
|
|
|
'obfs_param': proxyInfo['obfsParam']
|
|
|
|
}
|
|
|
|
return ['ssr-local', '-c', configFile], json.dumps(config), {}
|