feat: Trojan Reality support and protocol distribution optimizations

This commit is contained in:
xboard
2026-03-23 14:56:41 +08:00
parent a712be7cd4
commit 7dacb69275
15 changed files with 727 additions and 306 deletions
+159 -70
View File
@@ -2,6 +2,7 @@
namespace App\Protocols;
use App\Utils\Helper;
use App\Support\AbstractProtocol;
use App\Models\Server;
@@ -11,7 +12,10 @@ class QuantumultX extends AbstractProtocol
public $allowedProtocols = [
Server::TYPE_SHADOWSOCKS,
Server::TYPE_VMESS,
Server::TYPE_VLESS,
Server::TYPE_TROJAN,
Server::TYPE_SOCKS,
Server::TYPE_HTTP,
];
public function handle()
@@ -20,15 +24,15 @@ class QuantumultX extends AbstractProtocol
$user = $this->user;
$uri = '';
foreach ($servers as $item) {
if ($item['type'] === Server::TYPE_SHADOWSOCKS) {
$uri .= self::buildShadowsocks($item['password'], $item);
}
if ($item['type'] === Server::TYPE_VMESS) {
$uri .= self::buildVmess($item['password'], $item);
}
if ($item['type'] === Server::TYPE_TROJAN) {
$uri .= self::buildTrojan($item['password'], $item);
}
$uri .= match ($item['type']) {
Server::TYPE_SHADOWSOCKS => self::buildShadowsocks($item['password'], $item),
Server::TYPE_VMESS => self::buildVmess($item['password'], $item),
Server::TYPE_VLESS => self::buildVless($item['password'], $item),
Server::TYPE_TROJAN => self::buildTrojan($item['password'], $item),
Server::TYPE_SOCKS => self::buildSocks5($item['password'], $item),
Server::TYPE_HTTP => self::buildHttp($item['password'], $item),
default => ''
};
}
return response(base64_encode($uri))
->header('content-type', 'text/plain')
@@ -39,18 +43,16 @@ class QuantumultX extends AbstractProtocol
{
$protocol_settings = $server['protocol_settings'];
$password = data_get($server, 'password', $password);
$addr = Helper::wrapIPv6($server['host']);
$config = [
"shadowsocks={$server['host']}:{$server['port']}",
"shadowsocks={$addr}:{$server['port']}",
"method={$protocol_settings['cipher']}",
"password={$password}",
'fast-open=true',
'udp-relay=true',
"tag={$server['name']}"
];
if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) {
$plugin = data_get($protocol_settings, 'plugin');
$pluginOpts = data_get($protocol_settings, 'plugin_opts', '');
// 解析插件选项
$parsedOpts = collect(explode(';', $pluginOpts))
->filter()
->mapWithKeys(function ($pair) {
@@ -61,83 +63,170 @@ class QuantumultX extends AbstractProtocol
return [trim($key) => trim($value)];
})
->all();
switch ($plugin) {
case 'obfs':
if ($plugin === 'obfs') {
if (isset($parsedOpts['obfs'])) {
$config[] = "obfs={$parsedOpts['obfs']}";
if (isset($parsedOpts['obfs-host'])) {
$config[] = "obfs-host={$parsedOpts['obfs-host']}";
}
if (isset($parsedOpts['path'])) {
$config[] = "obfs-uri={$parsedOpts['path']}";
}
break;
}
if (isset($parsedOpts['obfs-host'])) {
$config[] = "obfs-host={$parsedOpts['obfs-host']}";
}
if (isset($parsedOpts['path'])) {
$config[] = "obfs-uri={$parsedOpts['path']}";
}
}
}
$uri = implode(',', $config);
$uri .= "\r\n";
return $uri;
self::applyCommonSettings($config, $server);
return implode(',', array_filter($config)) . "\r\n";
}
public static function buildVmess($uuid, $server)
{
$protocol_settings = $server['protocol_settings'];
$addr = Helper::wrapIPv6($server['host']);
$config = [
"vmess={$server['host']}:{$server['port']}",
'method=chacha20-poly1305',
"vmess={$addr}:{$server['port']}",
"method=" . data_get($protocol_settings, 'cipher', 'auto'),
"password={$uuid}",
'fast-open=true',
'udp-relay=true',
"tag={$server['name']}"
];
if (data_get($protocol_settings, 'tls')) {
if (data_get($protocol_settings, 'network') === 'tcp')
array_push($config, 'obfs=over-tls');
if (data_get($protocol_settings, 'tls_settings')) {
if (data_get($protocol_settings, 'tls_settings.allow_insecure'))
array_push($config, 'tls-verification=' . ($protocol_settings['tls_settings']['allow_insecure'] ? 'false' : 'true'));
if (data_get($protocol_settings, 'tls_settings.server_name'))
$host = data_get($protocol_settings, 'tls_settings.server_name');
}
}
if (data_get($protocol_settings, 'network') === 'ws') {
if (data_get($protocol_settings, 'tls'))
array_push($config, 'obfs=wss');
else
array_push($config, 'obfs=ws');
if (data_get($protocol_settings, 'network_settings')) {
if (data_get($protocol_settings, 'network_settings.path'))
array_push($config, "obfs-uri={$protocol_settings['network_settings']['path']}");
if (data_get($protocol_settings, 'network_settings.headers.Host') && !isset($host))
$host = data_get($protocol_settings, 'network_settings.headers.Host');
}
}
if (isset($host)) {
array_push($config, "obfs-host={$host}");
self::applyTransportSettings($config, $protocol_settings);
self::applyCommonSettings($config, $server);
return implode(',', array_filter($config)) . "\r\n";
}
public static function buildVless($uuid, $server)
{
$protocol_settings = $server['protocol_settings'];
$addr = Helper::wrapIPv6($server['host']);
$config = [
"vless={$addr}:{$server['port']}",
'method=none',
"password={$uuid}",
];
self::applyTransportSettings($config, $protocol_settings);
if ($flow = data_get($protocol_settings, 'flow')) {
$config[] = "vless-flow={$flow}";
}
$uri = implode(',', $config);
$uri .= "\r\n";
return $uri;
self::applyCommonSettings($config, $server);
return implode(',', array_filter($config)) . "\r\n";
}
private static function applyTransportSettings(&$config, $settings, bool $nativeTls = false, ?array $tlsData = null)
{
$tlsMode = (int) data_get($settings, 'tls', 0);
$network = data_get($settings, 'network', 'tcp');
$host = null;
$isWs = $network === 'ws';
switch ($network) {
case 'ws':
$config[] = $tlsMode ? 'obfs=wss' : 'obfs=ws';
if ($path = data_get($settings, 'network_settings.path')) {
$config[] = "obfs-uri={$path}";
}
$host = data_get($settings, 'network_settings.headers.Host');
break;
case 'tcp':
$headerType = data_get($settings, 'network_settings.header.type', 'tcp');
if ($headerType === 'http') {
$config[] = 'obfs=http';
$paths = data_get($settings, 'network_settings.header.request.path', ['/']);
$config[] = 'obfs-uri=' . (is_array($paths) ? ($paths[0] ?? '/') : $paths);
$hostVal = data_get($settings, 'network_settings.header.request.headers.Host');
$host = is_array($hostVal) ? ($hostVal[0] ?? null) : $hostVal;
} elseif ($tlsMode) {
$config[] = $nativeTls ? 'over-tls=true' : 'obfs=over-tls';
}
break;
}
switch ($tlsMode) {
case 2: // Reality
$host = $host ?? data_get($settings, 'reality_settings.server_name');
if ($pubKey = data_get($settings, 'reality_settings.public_key')) {
$config[] = "reality-base64-pubkey={$pubKey}";
}
if ($shortId = data_get($settings, 'reality_settings.short_id')) {
$config[] = "reality-hex-shortid={$shortId}";
}
break;
case 1: // TLS
$resolved = $tlsData ?? (array) data_get($settings, 'tls_settings', []);
$allowInsecure = (bool) ($resolved['allow_insecure'] ?? false);
$config[] = 'tls-verification=' . ($allowInsecure ? 'false' : 'true');
$host = $host ?? ($resolved['server_name'] ?? null);
break;
}
if ($host) {
$config[] = ($nativeTls && !$isWs) ? "tls-host={$host}" : "obfs-host={$host}";
}
}
private static function applyCommonSettings(&$config, $server)
{
$config[] = 'fast-open=true';
if ($server['type'] !== Server::TYPE_HTTP) {
$config[] = 'udp-relay=true';
}
$config[] = "tag={$server['name']}";
}
public static function buildTrojan($password, $server)
{
$protocol_settings = $server['protocol_settings'];
$addr = Helper::wrapIPv6($server['host']);
$config = [
"trojan={$server['host']}:{$server['port']}",
"trojan={$addr}:{$server['port']}",
"password={$password}",
'over-tls=true',
$protocol_settings['server_name'] ? "tls-host={$protocol_settings['server_name']}" : "",
// Tips: allowInsecure=false = tls-verification=true
$protocol_settings['allow_insecure'] ? 'tls-verification=false' : 'tls-verification=true',
'fast-open=true',
'udp-relay=true',
"tag={$server['name']}"
];
$config = array_filter($config);
$uri = implode(',', $config);
$uri .= "\r\n";
return $uri;
$tlsData = [
'allow_insecure' => data_get($protocol_settings, 'allow_insecure', false),
'server_name' => data_get($protocol_settings, 'server_name'),
];
self::applyTransportSettings($config, $protocol_settings, true, $tlsData);
self::applyCommonSettings($config, $server);
return implode(',', array_filter($config)) . "\r\n";
}
public static function buildSocks5($password, $server)
{
$protocol_settings = $server['protocol_settings'];
$addr = Helper::wrapIPv6($server['host']);
$config = [
"socks5={$addr}:{$server['port']}",
"username={$password}",
"password={$password}",
];
self::applyTransportSettings($config, $protocol_settings, true);
self::applyCommonSettings($config, $server);
return implode(',', array_filter($config)) . "\r\n";
}
public static function buildHttp($password, $server)
{
$protocol_settings = $server['protocol_settings'];
$addr = Helper::wrapIPv6($server['host']);
$config = [
"http={$addr}:{$server['port']}",
"username={$password}",
"password={$password}",
];
self::applyTransportSettings($config, $protocol_settings, true);
self::applyCommonSettings($config, $server);
return implode(',', array_filter($config)) . "\r\n";
}
}