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'] != '':
h2Object['host'] = streamInfo['host'].split(',')
return {**{
'network': 'h2',
'network': 'http',
'httpSettings': h2Object
}, **secureFunc(streamInfo['secure'])}

3
ProxyBuilder/builder.py

@ -12,6 +12,7 @@ from ProxyBuilder import Shadowsocks
from ProxyBuilder import ShadowsocksR
from ProxyBuilder import VMess
from ProxyBuilder import VLESS
from ProxyBuilder import Trojan
libcPaths = [
'/usr/lib64/libc.so.6', # CentOS
@ -104,6 +105,8 @@ def build(proxyInfo: dict, configDir: str,
clientObj = VMess
elif proxyInfo['type'] == 'vless': # VLESS节点
clientObj = VLESS
elif proxyInfo['type'] == 'trojan': # Trojan节点
clientObj = Trojan
else: # 未知类型
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 VMess
from ProxyFilter import VLESS
from ProxyFilter import Trojan
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)
elif raw['type'] == 'vless':
return VLESS.vlessFilter(raw, isExtra)
elif raw['type'] == 'trojan':
return Trojan.trojanFilter(raw, isExtra)
else:
return False, 'Unknown proxy type'
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
},
'server': {
'network': 'h2',
'network': 'http',
'httpSettings': {
'host': [host],
'path': path

3
ProxyTester/tester.py

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

9
Test.py

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

358
docs/ProxyObject.md

@ -869,3 +869,361 @@
+ 缺省: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