diff --git a/app/Protocols/ClashMeta.php b/app/Protocols/ClashMeta.php index f76cc8acb..55ffdb7f6 100644 --- a/app/Protocols/ClashMeta.php +++ b/app/Protocols/ClashMeta.php @@ -495,9 +495,11 @@ public static function buildVless($password, $server) if ($path = data_get($protocol_settings, 'network_settings.path')) $xhttpOpts['path'] = $path; if ($host = data_get($protocol_settings, 'network_settings.host')) - $xhttpOpts['host'] = $host; + $xhttpOpts['headers'] = ['Host' => $host]; if ($mode = data_get($protocol_settings, 'network_settings.mode')) $xhttpOpts['mode'] = $mode; + if ($alpn = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.alpn')) + $xhttpOpts['alpn'] = is_array($alpn) ? $alpn : [$alpn]; if (!empty($xhttpOpts)) $array['xhttp-opts'] = $xhttpOpts; break; @@ -799,18 +801,19 @@ protected static function appendUtls(&$array, $protocol_settings) if ($utls = data_get($protocol_settings, 'utls')) { if (data_get($utls, 'enabled')) { $array['client-fingerprint'] = Helper::getTlsFingerprint($utls); + return; } } + // fallback: read fingerprint from downloadSettings + if ($fp = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.fingerprint')) { + $array['client-fingerprint'] = $fp; + } } protected static function appendEch(&$array, $ech): void { if ($normalized = Helper::normalizeEchSettings($ech)) { - $array['ech-opts'] = array_filter([ - 'enable' => true, - 'config' => Helper::toMihomoEchConfig(data_get($normalized, 'config')), - 'query-server-name' => data_get($normalized, 'query_server_name'), - ], fn($value) => $value !== null); + $array['ech-opts'] = ['enable' => true]; } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/app/Protocols/General.php b/app/Protocols/General.php index 458265d55..2339dabc1 100644 --- a/app/Protocols/General.php +++ b/app/Protocols/General.php @@ -95,8 +95,7 @@ public static function buildVmess($uuid, $server) } if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { $config['fp'] = $fp; - } - + } switch (data_get($protocol_settings, 'network')) { case 'tcp': if (data_get($protocol_settings, 'network_settings.header.type', 'none') !== 'none') { @@ -175,6 +174,8 @@ public static function buildVless($uuid, $server) $config['security'] = "tls"; if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { $config['fp'] = $fp; + } elseif ($fp = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.fingerprint')) { + $config['fp'] = $fp; } if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { $config['sni'] = $serverName; @@ -182,6 +183,28 @@ public static function buildVless($uuid, $server) if (data_get($protocol_settings, 'tls_settings.allow_insecure')) { $config['allowInsecure'] = '1'; } + // ALPN + if ($alpn = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.alpn')) { + $config['alpn'] = is_array($alpn) + ? implode(',', $alpn) + : $alpn; + } + // ECH + $ech = data_get($protocol_settings, 'tls_settings.ech'); + if (data_get($ech, 'enabled')) { + $queryServerName = data_get($ech, 'query_server_name', 'cloudflare-ech.com'); + $echConfig = data_get($ech, 'config', ''); + // Strip PEM headers and collapse to a single base64 string + $echBase64 = preg_replace('/-----[^-]+-----/', '', $echConfig); + $echBase64 = str_replace(["\r", "\n", ' '], '', trim($echBase64)); + if ($echBase64) { + // Embed the config directly; client uses it as echConfigList + $config['ech'] = $echBase64; + } else { + // Fall back to query-server-name so the client fetches via DoH + $config['ech'] = $queryServerName; + } + } break; case 2: //reality $config['security'] = "reality"; diff --git a/app/Protocols/Shadowrocket.php b/app/Protocols/Shadowrocket.php index e17aae315..6aff2ffe6 100644 --- a/app/Protocols/Shadowrocket.php +++ b/app/Protocols/Shadowrocket.php @@ -200,6 +200,15 @@ public static function buildVless($uuid, $server) } if ($fp = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { $config['fp'] = $fp; + } elseif ($fp = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.fingerprint')) { + $config['fp'] = $fp; + } + $ech = data_get($protocol_settings, 'tls_settings.ech'); + if (data_get($ech, 'enabled')) { + $echConfig = data_get($ech, 'config', ''); + $echBase64 = preg_replace('/-----[^-]+-----/', '', $echConfig); + $echBase64 = str_replace(["\r", "\n", ' '], '', trim($echBase64)); + $config['ech'] = $echBase64 ?: data_get($ech, 'query_server_name', 'cloudflare-ech.com'); } break; case 2: diff --git a/app/Protocols/Stash.php b/app/Protocols/Stash.php index ef788dfbc..2319e7187 100644 --- a/app/Protocols/Stash.php +++ b/app/Protocols/Stash.php @@ -288,6 +288,20 @@ public static function buildVmess($uuid, $server) if ($host = data_get($protocol_settings, 'network_settings.host')) $array['h2-opts']['host'] = is_array($host) ? $host : [$host]; break; + case 'xhttp': + $array['network'] = 'xhttp'; + $xhttpOpts = []; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $xhttpOpts['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $xhttpOpts['headers'] = ['Host' => $host]; + if ($mode = data_get($protocol_settings, 'network_settings.mode')) + $xhttpOpts['mode'] = $mode; + if ($alpn = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.alpn')) + $xhttpOpts['alpn'] = is_array($alpn) ? $alpn : [$alpn]; + if (!empty($xhttpOpts)) + $array['xhttp-opts'] = $xhttpOpts; + break; default: break; } @@ -307,6 +321,8 @@ public function buildVless($uuid, $server) if ($fingerprint = Helper::getTlsFingerprint(data_get($protocol_settings, 'utls'))) { $array['client-fingerprint'] = $fingerprint; + } elseif ($fingerprint = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.fingerprint')) { + $array['client-fingerprint'] = $fingerprint; } switch (data_get($protocol_settings, 'tls')) { @@ -316,6 +332,9 @@ public function buildVless($uuid, $server) if ($serverName = data_get($protocol_settings, 'tls_settings.server_name')) { $array['servername'] = $serverName; } + if (data_get($protocol_settings, 'tls_settings.ech.enabled')) { + $array['ech-opts'] = ['enable' => true]; + } break; case 2: $array['tls'] = true; @@ -366,6 +385,20 @@ public function buildVless($uuid, $server) if ($host = data_get($protocol_settings, 'network_settings.host')) $array['h2-opts']['host'] = is_array($host) ? $host : [$host]; break; + case 'xhttp': + $array['network'] = 'xhttp'; + $xhttpOpts = []; + if ($path = data_get($protocol_settings, 'network_settings.path')) + $xhttpOpts['path'] = $path; + if ($host = data_get($protocol_settings, 'network_settings.host')) + $xhttpOpts['headers'] = ['Host' => $host]; + if ($mode = data_get($protocol_settings, 'network_settings.mode')) + $xhttpOpts['mode'] = $mode; + if ($alpn = data_get($protocol_settings, 'network_settings.extra.downloadSettings.tlsSettings.alpn')) + $xhttpOpts['alpn'] = is_array($alpn) ? $alpn : [$alpn]; + if (!empty($xhttpOpts)) + $array['xhttp-opts'] = $xhttpOpts; + break; } return $array;