Browse Source

feat: add trojan support

master
Dnomd343 2 years ago
parent
commit
9062fce3bd
  1. 45
      ProxyBuilder/Trojan.py
  2. 2
      ProxyBuilder/V2ray.py
  3. 3
      ProxyBuilder/builder.py
  4. 84
      ProxyFilter/Trojan.py
  5. 3
      ProxyFilter/filter.py
  6. 143
      ProxyTester/Trojan.py
  7. 2
      ProxyTester/V2ray.py
  8. 3
      ProxyTester/tester.py
  9. 9
      Test.py
  10. 358
      docs/ProxyObject.md

45
ProxyBuilder/Trojan.py

@ -0,0 +1,45 @@
#!/usr/bin/python
# -*- coding:utf-8 -*-
import json
from ProxyBuilder import Xray
def load(proxyInfo: dict, socksPort: int, configFile: str) -> tuple[list, str, dict]:
"""
Trojan配置载入
proxyInfo: 节点信息
socksPort: 本地通讯端口
configFile: 配置文件路径
return startCommand, fileContent, envVar
"""
flowType = None
if proxyInfo['stream']['secure'] is not None and proxyInfo['stream']['secure']['type'] == 'xtls':
flowType = proxyInfo['stream']['secure']['flow']
if flowType == 'xtls-origin':
flowType = 'xtls-rprx-origin'
elif flowType == 'xtls-direct':
flowType = 'xtls-rprx-direct'
elif flowType == 'xtls-splice':
flowType = 'xtls-rprx-splice'
else:
raise Exception('Unknown XTLS flow')
if proxyInfo['stream']['secure']['udp443']:
flowType += '-udp443'
outboundConfig = {
'protocol': 'trojan',
'settings': {
'servers': [
{
'address': proxyInfo['server'],
'port': proxyInfo['port'],
'password': proxyInfo['passwd'],
}
]
},
'streamSettings': Xray.xrayStreamConfig(proxyInfo['stream'])
}
if flowType is not None: # 添加XTLS流控类型
outboundConfig['settings']['servers'][0]['flow'] = flowType
config = Xray.baseConfig(socksPort, outboundConfig) # Trojan节点配置
return ['xray', '-c', configFile], json.dumps(config), {}

2
ProxyBuilder/V2ray.py

@ -101,7 +101,7 @@ def h2Config(streamInfo: dict, secureFunc) -> dict: # HTTP/2传输方式配置
if streamInfo['host'] != '': if streamInfo['host'] != '':
h2Object['host'] = streamInfo['host'].split(',') h2Object['host'] = streamInfo['host'].split(',')
return {**{ return {**{
'network': 'h2', 'network': 'http',
'httpSettings': h2Object 'httpSettings': h2Object
}, **secureFunc(streamInfo['secure'])} }, **secureFunc(streamInfo['secure'])}

3
ProxyBuilder/builder.py

@ -12,6 +12,7 @@ from ProxyBuilder import Shadowsocks
from ProxyBuilder import ShadowsocksR from ProxyBuilder import ShadowsocksR
from ProxyBuilder import VMess from ProxyBuilder import VMess
from ProxyBuilder import VLESS from ProxyBuilder import VLESS
from ProxyBuilder import Trojan
libcPaths = [ libcPaths = [
'/usr/lib64/libc.so.6', # CentOS '/usr/lib64/libc.so.6', # CentOS
@ -104,6 +105,8 @@ def build(proxyInfo: dict, configDir: str,
clientObj = VMess clientObj = VMess
elif proxyInfo['type'] == 'vless': # VLESS节点 elif proxyInfo['type'] == 'vless': # VLESS节点
clientObj = VLESS clientObj = VLESS
elif proxyInfo['type'] == 'trojan': # Trojan节点
clientObj = Trojan
else: # 未知类型 else: # 未知类型
return False, 'Unknown proxy type' return False, 'Unknown proxy type'

84
ProxyFilter/Trojan.py

@ -0,0 +1,84 @@
#!/usr/bin/python
# -*- coding:utf-8 -*-
from ProxyFilter import baseFunc
from ProxyFilter import Xray
trojanFilterRules = {
'rootObject': {
'remark': {
'optional': False,
'default': '',
'type': str,
'format': baseFunc.toStr
},
'server': {
'optional': True,
'type': str,
'format': baseFunc.toStrTidy,
'filter': baseFunc.isHost,
'errMsg': 'Illegal server address'
},
'port': {
'optional': True,
'type': int,
'format': baseFunc.toInt,
'filter': baseFunc.isPort,
'errMsg': 'Illegal port number'
},
'passwd': {
'optional': True,
'type': str,
'format': baseFunc.toStr
},
'stream': {
'optional': False,
'default': {
'type': 'tcp'
},
'type': [
'tcpObject',
'kcpObject',
'wsObject',
'h2Object',
'quicObject',
'grpcObject',
]
}
}
}
def trojanFilter(rawInfo: dict, isExtra: bool) -> tuple[bool, str or dict]:
"""
Trojan节点合法性检查
不合法:
return False, {reason}
合法:
return True, {
'type': 'trojan',
...
}
"""
try:
if not isExtra: # 去除非必要参数
trojanFilterRules['rootObject'].pop('remark')
for key, obj in Xray.xrayStreamRules.items(): # xray.stream -> trojan
trojanFilterRules[key] = obj
status, result = baseFunc.ruleFilter(rawInfo, trojanFilterRules, {
'type': 'trojan'
})
if not status: # 节点格式错误
return False, result
stream = result['stream']
if stream['secure'] is not None and stream['secure']['sni'] == '': # 未指定SNI
if stream['type'] == 'tcp' and stream['obfs'] is not None:
stream['secure']['sni'] = stream['obfs']['host'].split(',')[0]
elif stream['type'] == 'ws':
stream['secure']['sni'] = stream['host']
elif stream['type'] == 'h2':
stream['secure']['sni'] = stream['host'].split(',')[0]
return True, result
except:
return False, 'Unknown error'

3
ProxyFilter/filter.py

@ -5,6 +5,7 @@ from ProxyFilter import Shadowsocks
from ProxyFilter import ShadowsocksR from ProxyFilter import ShadowsocksR
from ProxyFilter import VMess from ProxyFilter import VMess
from ProxyFilter import VLESS from ProxyFilter import VLESS
from ProxyFilter import Trojan
def filte(raw: dict, isExtra: bool = False) -> tuple[bool, str or dict]: def filte(raw: dict, isExtra: bool = False) -> tuple[bool, str or dict]:
""" """
@ -31,6 +32,8 @@ def filte(raw: dict, isExtra: bool = False) -> tuple[bool, str or dict]:
return VMess.vmessFilter(raw, isExtra) return VMess.vmessFilter(raw, isExtra)
elif raw['type'] == 'vless': elif raw['type'] == 'vless':
return VLESS.vlessFilter(raw, isExtra) return VLESS.vlessFilter(raw, isExtra)
elif raw['type'] == 'trojan':
return Trojan.trojanFilter(raw, isExtra)
else: else:
return False, 'Unknown proxy type' return False, 'Unknown proxy type'
except: except:

143
ProxyTester/Trojan.py

@ -0,0 +1,143 @@
#!/usr/bin/python
# -*- coding:utf-8 -*-
import json
from ProxyTester import Xray
config = {}
def trojanBasicTest() -> dict:
serverConfig = {
'run_type': 'server',
'local_addr': '127.0.0.1',
'local_port': config['port'],
'password': [
config['passwd']
],
'ssl': {
'cert': config['cert'],
'key': config['key']
}
}
return {
'caption': 'Trojan basic',
'proxy': {
'type': 'trojan',
'server': '127.0.0.1',
'port': config['port'],
'passwd': config['passwd'],
'stream': {
'type': 'tcp',
'secure': {
'type': 'tls',
'sni': config['host']
}
}
},
'server': {
'startCommand': ['trojan', '-c', config['file']],
'fileContent': json.dumps(serverConfig),
'filePath': config['file'],
'envVar': {}
},
'aider': None
}
def loadTrojanStream(streamInfo: dict, xtlsFlow: str or None) -> dict:
proxyInfo = {
'type': 'trojan',
'server': '127.0.0.1',
'port': config['port'],
'passwd': config['passwd'],
'stream': streamInfo['client']
}
inboundConfig = {
'protocol': 'trojan',
'listen': '127.0.0.1',
'port': config['port'],
'settings': {
'clients': [
{
'password': config['passwd']
}
]
},
'streamSettings': streamInfo['server']
}
if xtlsFlow is not None: # add XTLS flow option
inboundConfig['settings']['clients'][0]['flow'] = xtlsFlow
return {
'caption': 'Trojan network ' + streamInfo['caption'],
'proxy': proxyInfo,
'server': {
'startCommand': ['xray', '-c', config['file']],
'fileContent': Xray.xrayConfig(inboundConfig),
'filePath': config['file'],
'envVar': {}
},
'aider': None
}
def trojanTest(trojanConfig: dict) -> list:
result = []
for key, value in trojanConfig.items(): # trojanConfig -> config
config[key] = value
result.append(trojanBasicTest()) # basic test
# TCP stream
streamInfo = Xray.loadTcpStream(False, '', '')
result.append(loadTrojanStream(streamInfo, None))
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
for flow in Xray.xtlsFlowList:
streamInfo = Xray.loadTcpStream(False, '', '')
xtlsFlow, streamInfo = Xray.addXtlsConfig(streamInfo, config['cert'], config['key'], config['host'], flow)
result.append(loadTrojanStream(streamInfo, xtlsFlow))
streamInfo = Xray.loadTcpStream(True, config['host'], '/')
result.append(loadTrojanStream(streamInfo, None))
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
# mKCP stream
for obfs in Xray.udpObfsList:
streamInfo = Xray.loadKcpStream(config['passwd'], obfs)
result.append(loadTrojanStream(streamInfo, None))
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
for flow in Xray.xtlsFlowList:
streamInfo = Xray.loadKcpStream(config['passwd'], obfs)
xtlsFlow, streamInfo = Xray.addXtlsConfig(streamInfo, config['cert'], config['key'], config['host'], flow)
result.append(loadTrojanStream(streamInfo, xtlsFlow))
# WebSocket stream
streamInfo = Xray.loadWsStream(config['host'], config['path'], False)
result.append(loadTrojanStream(streamInfo, None))
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
streamInfo = Xray.loadWsStream(config['host'], config['path'], True)
result.append(loadTrojanStream(streamInfo, None))
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
# HTTP/2 stream
streamInfo = Xray.loadH2Stream(config['host'], config['path'])
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
# QUIC stream
for method in Xray.quicMethodList:
for obfs in Xray.udpObfsList:
streamInfo = Xray.loadQuicStream(method, config['passwd'], obfs)
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
# GRPC stream
streamInfo = Xray.loadGrpcStream(config['service'])
result.append(loadTrojanStream(streamInfo, None))
streamInfo = Xray.addTlsConfig(streamInfo, config['cert'], config['key'], config['host'])
result.append(loadTrojanStream(streamInfo, None))
return result

2
ProxyTester/V2ray.py

@ -145,7 +145,7 @@ def loadH2Stream(host: str, path: str) -> dict:
'path': path 'path': path
}, },
'server': { 'server': {
'network': 'h2', 'network': 'http',
'httpSettings': { 'httpSettings': {
'host': [host], 'host': [host],
'path': path 'path': path

3
ProxyTester/tester.py

@ -5,6 +5,7 @@ from ProxyTester import Shadowsocks
from ProxyTester import ShadowsocksR from ProxyTester import ShadowsocksR
from ProxyTester import VMess from ProxyTester import VMess
from ProxyTester import VLESS from ProxyTester import VLESS
from ProxyTester import Trojan
def test(key: str, config: dict) -> list: def test(key: str, config: dict) -> list:
if key in ['ss', 'shadowsocks']: if key in ['ss', 'shadowsocks']:
@ -15,5 +16,7 @@ def test(key: str, config: dict) -> list:
return VMess.vmessTest(config) return VMess.vmessTest(config)
elif key == 'vless': elif key == 'vless':
return VLESS.vlessTest(config) return VLESS.vlessTest(config)
elif key == 'trojan':
return Trojan.trojanTest(config)
else: else:
return [] return []

9
Test.py

@ -52,12 +52,15 @@ def testObject(option: dict) -> None: # test target object
}) })
print(option['caption'], end=' -> ') print(option['caption'], end=' -> ')
if not checkResult['success']: # client build error if not checkResult['success']: # client build error
print('\n----------------------------------------------------------------') print('\n--------------------------------------------------------------------------------------------------------------------------------')
print(option) print(option)
print('----------------------------------------------------------------\n') print('--------------------------------------------------------------------------------------------------------------------------------\n')
testDestroy(option['server'], serverProcess) # destroy and exit
if option['aider'] is not None:
testDestroy(option['aider'], aiderProcess)
raise Exception('check error') raise Exception('check error')
delay = checkResult['check']['http']['delay'] # get http delay delay = checkResult['check']['http']['delay'] # get http delay
print(str(delay) + 'ms') print(format(delay, '.2f') + 'ms')
testDestroy(option['server'], serverProcess) # destroy server process testDestroy(option['server'], serverProcess) # destroy server process
if option['aider'] is not None: if option['aider'] is not None:

358
docs/ProxyObject.md

@ -869,3 +869,361 @@
+ 缺省:False + 缺省:False
+ 可选值:不限 + 可选值:不限
+ 建议值:False + 建议值:False
## Trojan
> **remark**
>
> + 类型:*str*
> + 说明:节点备注名称
> + 缺省:''
> + 可选值:不限
```
{
'type': 'trojan',
'server': ...,
'port': ...,
'passwd': ...,
'stream': ...
}
```
**server**
+ 类型:*str*
+ 说明:服务器地址
+ 缺省:必选
+ 可选值:合法的IP地址或域名
**port**
+ 类型:*int*
+ 说明:服务器端口
+ 缺省:必选
+ 可选值:1 ~ 65535
**passwd**
+ 类型:*str*
+ 说明:Trojan连接密码
+ 缺省:必选
+ 可选值:不限
**stream**
+ 类型:*tcpObject* / *kcpObject* / *wsObject* / *h2Object* / *quicObject* / *grpcObject*
+ 说明:Trojan底层传输方式
+ 缺省:tcpObject
+ 可选值:不限
### tcpObject
```
{
'type': 'tcp',
'obfs': ...,
'secure': ...
}
```
**obfs**
+ 类型:*None* / *obfsObject*
+ 说明:http伪装
+ 缺省:None
+ 可选值:不限
**secure**
+ 类型:*None* / *tlsObject* / *xtlsObject*
+ 说明:TLS加密
+ 缺省:None
+ 可选值:不限
### kcpObject
```
{
'type': 'kcp',
'seed': ...,
'obfs': ...,
'secure': ...
}
```
**seed**
+ 类型:*None* / *str*
+ 说明:mKCP混淆密码
+ 缺省:None
+ 可选值:不限
**obfs**
+ 类型:*str*
+ 说明:数据包头部伪装类型
+ 缺省:'none'
+ 可选值:`none`,`srtp`,`utp`,`wechat-video`,`dtls`,`wireguard`
**secure**
+ 类型:*None* / *tlsObject* / *xtlsObject*
+ 说明:TLS加密
+ 缺省:None
+ 可选值:不限
### wsObject
```
{
'type': 'ws',
'host': ...,
'path': ...,
'ed': ...,
'secure': ...
}
```
**host**
+ 类型:*str*
+ 说明:Websocket连接域名
+ 缺省:''
+ 可选值:不限
+ 建议值:合法域名
**path**
+ 类型:*str*
+ 说明:Websocket连接路径
+ 缺省:'/'
+ 可选值:不限
+ 建议值:以`/`开头的合法路径
**ed**
+ 类型:*None* / *int*
+ 说明:`Early Data`长度阈值
+ 缺省:None
+ 可选值:>0
+ 建议值:2048
**secure**
+ 类型:*None* / *tlsObject*
+ 说明:TLS加密
+ 缺省:None
+ 可选值:不限
### h2Object
```
{
'type': 'h2',
'host': ...,
'path': ...,
'secure': ...
}
```
**host**
+ 类型:*str*
+ 说明:HTTP/2通讯域名
+ 缺省:''
+ 可选值:不限
+ 建议值:合法域名列表(逗号隔开)
**path**
+ 类型:*str*
+ 说明:HTTP/2通讯路径
+ 缺省:'/'
+ 可选值:不限
+ 建议值:以`/`开头的合法路径
**secure**
+ 类型:*tlsObject*
+ 说明:TLS加密
+ 缺省:None
+ 可选值:不限
### quicObject
```
{
'type': 'quic',
'method': ...,
'passwd': ...,
'obfs': ...,
'secure': ...
}
```
**method**
+ 类型:*str*
+ 说明:QUIC加密方式
+ 缺省:'none'
+ 可选值:`none`,`aes-128-gcm`,`chacha20-poly1305`
**passwd**
+ 类型:*str*
+ 说明:QUIC连接密码
+ 缺省:''
+ 可选值:不限
**obfs**
+ 类型:*str*
+ 说明:数据包头部伪装类型
+ 缺省:'none'
+ 可选值:`none`,`srtp`,`utp`,`wechat-video`,`dtls`,`wireguard`
**secure**
+ 类型:*tlsObject*
+ 说明:TLS加密
+ 缺省:secureObject
+ 可选值:不限
### grpcObject
```
{
'type': 'grpc',
'service': ...,
'secure': ...
}
```
**service**
+ 类型:*str*
+ 说明:gRPC服务名称
+ 缺省:必选
+ 可选值:不限
+ 建议值:英文大小写字母、数字、下划线及英文句号组成
**secure**
+ 类型:*None* / *tlsObject*
+ 说明:TLS加密
+ 缺省:None
+ 可选值:不限
### obfsObject
```
{
'host': ...,
'path': ...
}
```
**host**
+ 类型:*str*
+ 说明:http伪装域名
+ 缺省:''
+ 可选值:不限
+ 建议值:合法域名列表(逗号隔开)
**path**
+ 类型:*str*
+ 说明:http伪装路径
+ 缺省:'/'
+ 可选值:不限
+ 建议值:以`/`开头的合法路径
### tlsObject
```
{
'type': 'tls',
'sni': ...,
'alpn': ...,
'verify': ...
}
```
**sni**
+ 类型:*str*
+ 说明:TLS握手SNI字段
+ 缺省:obfsObject.host[0] / wsObject.host / h2Object.host[0] / ''
+ 可选值:不限
+ 建议值:合法域名
**alpn**
+ 类型:*None* / *str*
+ 说明:TLS握手协商协议
+ 缺省:None
+ 可选值:`h2`,`http/1.1`,`h2,http/1.1`
+ 建议值:'h2,http/1.1'
**verify**
+ 类型:*bool*
+ 说明:是否验证服务端证书
+ 缺省:True
+ 可选值:True / False
+ 建议值:True
### xtlsObject
```
{
'type': 'xtls',
'sni': ...,
'alpn': ...,
'verify': ...,
'flow': ...,
'udp443': ...
}
```
**sni**
+ 类型:*str*
+ 说明:TLS握手SNI字段
+ 缺省:obfsObject.host[0] / wsObject.host / h2Object.host[0] / ''
+ 可选值:不限
+ 建议值:合法域名
**alpn**
+ 类型:*None* / *str*
+ 说明:TLS握手协商协议
+ 缺省:None
+ 可选值:`h2`,`http/1.1`,`h2,http/1.1`
+ 建议值:'h2,http/1.1'
**verify**
+ 类型:*bool*
+ 说明:是否验证服务端证书
+ 缺省:True
+ 可选值:不限
+ 建议值:True
**flow**
+ 类型:*str*
+ 说明:XTLS流控算法
+ 缺省:'xtls-direct'
+ 可选值:`xtls-origin`,`xtls-direct`,`xtls-splice`
+ 建议值:'xtls-direct' (Linux平台建议`xtls-splice`)
**udp443**
+ 类型:*bool*
+ 说明:是否放行UDP/443端口流量
+ 缺省:False
+ 可选值:不限
+ 建议值:False

Loading…
Cancel
Save