diff --git a/backend/package.json b/backend/package.json index c7da15bbf..e55c31f0b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.14.75", + "version": "2.14.76", "description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.", "main": "src/main.js", "scripts": { diff --git a/backend/src/core/proxy-utils/parsers/index.js b/backend/src/core/proxy-utils/parsers/index.js index d1a3487b6..c2fbc76ab 100644 --- a/backend/src/core/proxy-utils/parsers/index.js +++ b/backend/src/core/proxy-utils/parsers/index.js @@ -448,7 +448,7 @@ function URI_Hysteria2() { proxy['obfs-password'] = params['obfs-password']; proxy['skip-cert-verify'] = /(TRUE)|1/i.test(params.insecure); proxy.tfo = /(TRUE)|1/i.test(params.fastopen); - proxy.fingerprint = params.pinSHA256; + proxy['tls-fingerprint'] = params.pinSHA256; return proxy; }; diff --git a/backend/src/core/proxy-utils/parsers/peggy/surge.js b/backend/src/core/proxy-utils/parsers/peggy/surge.js index ba8ca262c..ef6f0f456 100644 --- a/backend/src/core/proxy-utils/parsers/peggy/surge.js +++ b/backend/src/core/proxy-utils/parsers/peggy/surge.js @@ -36,7 +36,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v return proxy; } -shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/others)* { +shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "ss"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -46,7 +46,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ $set(proxy, "plugin-opts.path", obfs.path); } } -vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/others)* { +vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "vmess"; proxy.cipher = proxy.cipher || "none"; if (proxy.aead) { @@ -56,18 +56,18 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/ } handleWebsocket(); } -trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/others)* { +trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "trojan"; handleWebsocket(); } -https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/others)* { +https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "http"; proxy.tls = true; } -http = tag equals "http" address (username password)? (fast_open/others)* { +http = tag equals "http" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "http"; } -snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/others)* { +snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "snell"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -76,23 +76,23 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_ $set(proxy, "obfs-opts.path", obfs.path); } } -tuic = tag equals "tuic" address (alpn/token/ip_version/tls_verification/sni/fast_open/tfo/ecn/others)* { +tuic = tag equals "tuic" address (alpn/token/ip_version/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "tuic"; } -tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_verification/sni/fast_open/tfo/ecn/others)* { +tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "tuic"; proxy.version = 5; } -wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/others)* { +wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "wireguard-surge"; } -hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/download_bandwidth/ecn/others)* { +hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "hysteria2"; } -socks5 = tag equals "socks5" address (username password)? (fast_open/others)* { +socks5 = tag equals "socks5" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "socks5"; } -socks5_tls = tag equals "socks5-tls" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/others)* { +socks5_tls = tag equals "socks5-tls" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "socks5"; proxy.tls = true; } @@ -200,6 +200,9 @@ no_error_alert = comma "no-error-alert" equals match:[^,]+ { proxy["no-error-ale underlying_proxy = comma "underlying-proxy" equals match:[^,]+ { proxy["underlying-proxy"] = match.join(""); } download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); } test_url = comma "test-url" equals match:[^,]+ { proxy["test-url"] = match.join(""); } +shadow_tls_version = comma "shadow-tls-version" equals match:$[0-9]+ { proxy["shadow-tls-version"] = parseInt(match.trim()); } +shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-sni"] = match.join(""); } +shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); } token = comma "token" equals match:[^,]+ { proxy.token = match.join(""); } alpn = comma "alpn" equals match:[^,]+ { proxy.alpn = match.join(""); } uuidk = comma "uuid" equals match:[^,]+ { proxy.uuid = match.join(""); } diff --git a/backend/src/core/proxy-utils/parsers/peggy/surge.peg b/backend/src/core/proxy-utils/parsers/peggy/surge.peg index 6a1255402..c7cd40d3c 100644 --- a/backend/src/core/proxy-utils/parsers/peggy/surge.peg +++ b/backend/src/core/proxy-utils/parsers/peggy/surge.peg @@ -34,7 +34,7 @@ start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v return proxy; } -shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/others)* { +shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "ss"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -44,7 +44,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ $set(proxy, "plugin-opts.path", obfs.path); } } -vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/others)* { +vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "vmess"; proxy.cipher = proxy.cipher || "none"; if (proxy.aead) { @@ -54,18 +54,18 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/ } handleWebsocket(); } -trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/others)* { +trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "trojan"; handleWebsocket(); } -https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/others)* { +https = tag equals "https" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "http"; proxy.tls = true; } -http = tag equals "http" address (username password)? (fast_open/others)* { +http = tag equals "http" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "http"; } -snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/others)* { +snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "snell"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -74,23 +74,23 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_ $set(proxy, "obfs-opts.path", obfs.path); } } -tuic = tag equals "tuic" address (alpn/token/ip_version/tls_verification/sni/fast_open/tfo/ecn/others)* { +tuic = tag equals "tuic" address (alpn/token/ip_version/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "tuic"; } -tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_verification/sni/fast_open/tfo/ecn/others)* { +tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "tuic"; proxy.version = 5; } -wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/others)* { +wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "wireguard-surge"; } -hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/download_bandwidth/ecn/others)* { +hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "hysteria2"; } -socks5 = tag equals "socks5" address (username password)? (fast_open/others)* { +socks5 = tag equals "socks5" address (username password)? (fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "socks5"; } -socks5_tls = tag equals "socks5-tls" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/others)* { +socks5_tls = tag equals "socks5-tls" address (username password)? (sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/others)* { proxy.type = "socks5"; proxy.tls = true; } @@ -198,6 +198,9 @@ no_error_alert = comma "no-error-alert" equals match:[^,]+ { proxy["no-error-ale underlying_proxy = comma "underlying-proxy" equals match:[^,]+ { proxy["underlying-proxy"] = match.join(""); } download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); } test_url = comma "test-url" equals match:[^,]+ { proxy["test-url"] = match.join(""); } +shadow_tls_version = comma "shadow-tls-version" equals match:$[0-9]+ { proxy["shadow-tls-version"] = parseInt(match.trim()); } +shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-sni"] = match.join(""); } +shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); } token = comma "token" equals match:[^,]+ { proxy.token = match.join(""); } alpn = comma "alpn" equals match:[^,]+ { proxy.alpn = match.join(""); } uuidk = comma "uuid" equals match:[^,]+ { proxy.uuid = match.join(""); } diff --git a/backend/src/core/proxy-utils/producers/clash.js b/backend/src/core/proxy-utils/producers/clash.js index ac5b7b58d..1e55e04fa 100644 --- a/backend/src/core/proxy-utils/producers/clash.js +++ b/backend/src/core/proxy-utils/producers/clash.js @@ -97,6 +97,10 @@ export default function Clash_Producer() { ) { delete proxy.tls; } + + if (proxy['tls-fingerprint']) { + proxy.fingerprint = proxy['tls-fingerprint']; + } delete proxy['tls-fingerprint']; delete proxy.subName; delete proxy.collectionName; diff --git a/backend/src/core/proxy-utils/producers/clashmeta.js b/backend/src/core/proxy-utils/producers/clashmeta.js index 292b36174..576b0d159 100644 --- a/backend/src/core/proxy-utils/producers/clashmeta.js +++ b/backend/src/core/proxy-utils/producers/clashmeta.js @@ -116,6 +116,9 @@ export default function ClashMeta_Producer() { delete proxy.tls; } + if (proxy['tls-fingerprint']) { + proxy.fingerprint = proxy['tls-fingerprint']; + } delete proxy['tls-fingerprint']; delete proxy.subName; delete proxy.collectionName; diff --git a/backend/src/core/proxy-utils/producers/shadowrocket.js b/backend/src/core/proxy-utils/producers/shadowrocket.js index c61395910..e5484b3bf 100644 --- a/backend/src/core/proxy-utils/producers/shadowrocket.js +++ b/backend/src/core/proxy-utils/producers/shadowrocket.js @@ -116,6 +116,9 @@ export default function ShadowRocket_Producer() { delete proxy.tls; } + if (proxy['tls-fingerprint']) { + proxy.fingerprint = proxy['tls-fingerprint']; + } delete proxy['tls-fingerprint']; delete proxy.subName; delete proxy.collectionName; diff --git a/backend/src/core/proxy-utils/producers/stash.js b/backend/src/core/proxy-utils/producers/stash.js index 53ec827a7..fc562b3bd 100644 --- a/backend/src/core/proxy-utils/producers/stash.js +++ b/backend/src/core/proxy-utils/producers/stash.js @@ -174,6 +174,9 @@ export default function Stash_Producer() { ) { delete proxy.tls; } + if (proxy['tls-fingerprint']) { + proxy.fingerprint = proxy['tls-fingerprint']; + } delete proxy['tls-fingerprint']; delete proxy.subName; delete proxy.collectionName; diff --git a/backend/src/core/proxy-utils/producers/surge.js b/backend/src/core/proxy-utils/producers/surge.js index 918255dad..be8015be4 100644 --- a/backend/src/core/proxy-utils/producers/surge.js +++ b/backend/src/core/proxy-utils/producers/surge.js @@ -73,7 +73,22 @@ function shadowsocks(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -115,7 +130,22 @@ function trojan(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -164,7 +194,22 @@ function vmess(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -202,7 +247,22 @@ function http(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -242,7 +302,22 @@ function socks5(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -277,7 +352,22 @@ function snell(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -319,6 +409,12 @@ function tuic(proxy) { 'skip-cert-verify', ); + // tls fingerprint + result.appendIfPresent( + `,server-cert-fingerprint-sha256=${proxy['tls-fingerprint']}`, + 'tls-fingerprint', + ); + // tfo if (isPresent(proxy, 'tfo')) { result.append(`,tfo=${proxy['tfo']}`); @@ -329,7 +425,22 @@ function tuic(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -362,7 +473,22 @@ function wireguard(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy', @@ -397,8 +523,8 @@ function hysteria2(proxy) { 'skip-cert-verify', ); result.appendIfPresent( - `,server-cert-fingerprint-sha256=${proxy.fingerprint}`, - 'fingerprint', + `,server-cert-fingerprint-sha256=${proxy['tls-fingerprint']}`, + 'tls-fingerprint', ); // tfo @@ -411,7 +537,22 @@ function hysteria2(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + // shadow-tls + if (isPresent(proxy, 'shadow-tls-password')) { + result.append(`,shadow-tls-password=${proxy['shadow-tls-password']}`); + + result.appendIfPresent( + `,shadow-tls-version=${proxy['shadow-tls-version']}`, + 'shadow-tls-version', + ); + result.appendIfPresent( + `,shadow-tls-sni=${proxy['shadow-tls-sni']}`, + 'shadow-tls-sni', + ); + } + // underlying-proxy + result.appendIfPresent( `,underlying-proxy=${proxy['underlying-proxy']}`, 'underlying-proxy',