diff --git a/README.md b/README.md index a0e99b1..5757e21 100644 --- a/README.md +++ b/README.md @@ -26,15 +26,13 @@ Core functionalities: ### Supported Input Formats -- [x] SS URI -- [x] SSR URI -- [x] SSD URI -- [x] V2RayN URI -- [x] Hysteria 2 URI +- [x] URI(SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5) +- [x] Clash Proxies YAML +- [x] Clash Proxy JSON(single line) - [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS) - [x] Loon (SS, SSR, VMess, Trojan, HTTP, SOCKS5, WireGuard, VLESS, Hysteria 2) -- [x] Surge (SS, VMess, Trojan, HTTP, SOCKS5, TUIC, Snell, Hysteria 2, SSR(external, only for macOS), External Proxy Program(only for macOS), WireGuard(Surge to Surge)) -- [x] Surfboard (SS, VMess, Trojan, HTTP, SOCKS5, WireGuard(Surfboard to Surfboard)) +- [x] Surge (SS, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, TUIC, Snell, Hysteria 2, SSH(Password authentication only), SSR(external, only for macOS), External Proxy Program(only for macOS), WireGuard(Surge to Surge)) +- [x] Surfboard (SS, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, WireGuard(Surfboard to Surfboard)) - [x] Shadowrocket (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC) - [x] Clash.Meta (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC) - [x] Stash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, TUIC) diff --git a/backend/package.json b/backend/package.json index df666bf..9e5b36c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.14.224", + "version": "2.14.225", "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 8aad8da..bd9cd26 100644 --- a/backend/src/core/proxy-utils/parsers/index.js +++ b/backend/src/core/proxy-utils/parsers/index.js @@ -752,6 +752,9 @@ function Clash_All() { if (proxy['benchmark-url']) { proxy['test-url'] = proxy['benchmark-url']; } + if (proxy['benchmark-timeout']) { + proxy['test-timeout'] = proxy['benchmark-timeout']; + } return proxy; }; @@ -1013,6 +1016,14 @@ function Loon_WireGuard() { return { name, test, parse }; } +function Surge_SSH() { + const name = 'Surge SSH Parser'; + const test = (line) => { + return /^.*=\s*ssh/.test(line.split(',')[0]); + }; + const parse = (line) => getSurgeParser().parse(line); + return { name, test, parse }; +} function Surge_SS() { const name = 'Surge SS Parser'; const test = (line) => { @@ -1183,6 +1194,7 @@ export default [ URI_Hysteria2(), URI_Trojan(), Clash_All(), + Surge_SSH(), Surge_SS(), Surge_VMess(), Surge_Trojan(), diff --git a/backend/src/core/proxy-utils/parsers/peggy/surge.js b/backend/src/core/proxy-utils/parsers/peggy/surge.js index b214422..7640cb4 100644 --- a/backend/src/core/proxy-utils/parsers/peggy/surge.js +++ b/backend/src/core/proxy-utils/parsers/peggy/surge.js @@ -37,11 +37,11 @@ const grammars = String.raw` } } -start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2) { +start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2/ssh) { return proxy; } -shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "ss"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -52,7 +52,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ } handleShadowTLS(); } -vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/test_url/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "vmess"; proxy.cipher = proxy.cipher || "none"; if (proxy.aead) { @@ -63,21 +63,25 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/ handleWebsocket(); handleShadowTLS(); } -trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "trojan"; handleWebsocket(); handleShadowTLS(); } -https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "http"; proxy.tls = true; handleShadowTLS(); } -http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/test_url/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "http"; handleShadowTLS(); } -snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +ssh = tag equals "ssh" address (username password)? (usernamek passwordk)? (server_fingerprint/idle_timeout/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { + proxy.type = "ssh"; + handleShadowTLS(); +} +snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "snell"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -87,28 +91,28 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_ } handleShadowTLS(); } -tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "tuic"; handleShadowTLS(); } -tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "tuic"; proxy.version = 5; handleShadowTLS(); } -wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "wireguard-surge"; handleShadowTLS(); } -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/block_quic/others)* { +hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "hysteria2"; handleShadowTLS(); } -socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "socks5"; handleShadowTLS(); } -socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "socks5"; proxy.tls = true; handleShadowTLS(); @@ -218,6 +222,14 @@ 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(""); } +test_udp = comma "test-udp" equals match:[^,]+ { proxy["test-udp"] = match.join(""); } +test_timeout = comma "test-timeout" equals match:$[0-9]+ { proxy["test-timeout"] = parseInt(match.trim()); } +tos = comma "tos" equals match:$[0-9]+ { proxy.tos = parseInt(match.trim()); } +interface = comma "interface" equals match:[^,]+ { proxy.interface = match.join(""); } +allow_other_interface = comma "allow-other-interface" equals flag:bool { proxy["allow-other-interface"] = flag; } +hybrid = comma "hybrid" equals flag:bool { proxy.hybrid = flag; } +idle_timeout = comma "idle-timeout" equals match:$[0-9]+ { proxy["idle-timeout"] = parseInt(match.trim()); } +server_fingerprint = comma "server-fingerprint" equals match:[^,]+ { proxy["server-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); } block_quic = comma "block-quic" equals match:[^,]+ { proxy["block-quic"] = 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(""); } diff --git a/backend/src/core/proxy-utils/parsers/peggy/surge.peg b/backend/src/core/proxy-utils/parsers/peggy/surge.peg index 0c62f79..5f36f7a 100644 --- a/backend/src/core/proxy-utils/parsers/peggy/surge.peg +++ b/backend/src/core/proxy-utils/parsers/peggy/surge.peg @@ -35,11 +35,11 @@ } } -start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2) { +start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5/wireguard/hysteria2/ssh) { return proxy; } -shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "ss"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -50,7 +50,7 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/ } handleShadowTLS(); } -vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/test_url/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "vmess"; proxy.cipher = proxy.cipher || "none"; if (proxy.aead) { @@ -61,21 +61,25 @@ vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/ handleWebsocket(); handleShadowTLS(); } -trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +trojan = tag equals "trojan" address (passwordk/ws/ws_path/ws_headers/tls/sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "trojan"; handleWebsocket(); handleShadowTLS(); } -https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +https = tag equals "https" address (username password)? (usernamek passwordk)? (sni/tls_fingerprint/tls_verification/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "http"; proxy.tls = true; handleShadowTLS(); } -http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/test_url/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +http = tag equals "http" address (username password)? (usernamek passwordk)? (ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "http"; handleShadowTLS(); } -snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/test_url/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +ssh = tag equals "ssh" address (username password)? (usernamek passwordk)? (server_fingerprint/idle_timeout/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { + proxy.type = "ssh"; + handleShadowTLS(); +} +snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_uri/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/udp_relay/reuse/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "snell"; // handle obfs if (obfs.type == "http" || obfs.type === "tls") { @@ -85,28 +89,28 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_ } handleShadowTLS(); } -tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "tuic"; handleShadowTLS(); } -tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/test_url/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "tuic"; proxy.version = 5; handleShadowTLS(); } -wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/test_url/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "wireguard-surge"; handleShadowTLS(); } -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/block_quic/others)* { +hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "hysteria2"; handleShadowTLS(); } -socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "socks5"; handleShadowTLS(); } -socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/test_url/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { +socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek passwordk)? (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_fingerprint/tls_verification/fast_open/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* { proxy.type = "socks5"; proxy.tls = true; handleShadowTLS(); @@ -216,6 +220,14 @@ 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(""); } +test_udp = comma "test-udp" equals match:[^,]+ { proxy["test-udp"] = match.join(""); } +test_timeout = comma "test-timeout" equals match:$[0-9]+ { proxy["test-timeout"] = parseInt(match.trim()); } +tos = comma "tos" equals match:$[0-9]+ { proxy.tos = parseInt(match.trim()); } +interface = comma "interface" equals match:[^,]+ { proxy.interface = match.join(""); } +allow_other_interface = comma "allow-other-interface" equals flag:bool { proxy["allow-other-interface"] = flag; } +hybrid = comma "hybrid" equals flag:bool { proxy.hybrid = flag; } +idle_timeout = comma "idle-timeout" equals match:$[0-9]+ { proxy["idle-timeout"] = parseInt(match.trim()); } +server_fingerprint = comma "server-fingerprint" equals match:[^,]+ { proxy["server-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); } block_quic = comma "block-quic" equals match:[^,]+ { proxy["block-quic"] = 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(""); } diff --git a/backend/src/core/proxy-utils/producers/sing-box.js b/backend/src/core/proxy-utils/producers/sing-box.js index 77bd068..c19fd5e 100644 --- a/backend/src/core/proxy-utils/producers/sing-box.js +++ b/backend/src/core/proxy-utils/producers/sing-box.js @@ -217,6 +217,31 @@ const tlsParser = (proxy, parsedProxy) => { if (!parsedProxy.tls.enabled) delete parsedProxy.tls; }; +const sshParser = (proxy = {}) => { + const parsedProxy = { + tag: proxy.name, + type: 'ssh', + server: proxy.server, + server_port: parseInt(`${proxy.port}`, 10), + }; + if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535) + throw 'invalid port'; + if (proxy.username) parsedProxy.user = proxy.username; + if (proxy.password) parsedProxy.password = proxy.password; + if (proxy['server-fingerprint']) { + parsedProxy.host_key = [proxy['server-fingerprint']]; + // https://manual.nssurge.com/policy/ssh.html + // Surge only supports curve25519-sha256 as the kex algorithm and aes128-gcm as the encryption algorithm. It means that the SSH server must use OpenSSH v7.3 or above. (It should not be a problem since OpenSSH 7.3 was released on 2016-08-01.) + // TODO: ? + parsedProxy.host_key_algorithms = [ + proxy['server-fingerprint'].split(' ')[0], + ]; + } + if (proxy['fast-open']) parsedProxy.udp_fragment = true; + tfoParser(proxy, parsedProxy); + return parsedProxy; +}; + const httpParser = (proxy = {}) => { const parsedProxy = { tag: proxy.name, @@ -624,6 +649,9 @@ export default function singbox_Producer() { .map((proxy) => { try { switch (proxy.type) { + case 'ssh': + list.push(sshParser(proxy)); + break; case 'http': list.push(httpParser(proxy)); break; diff --git a/backend/src/core/proxy-utils/producers/stash.js b/backend/src/core/proxy-utils/producers/stash.js index 3f0617a..27f8995 100644 --- a/backend/src/core/proxy-utils/producers/stash.js +++ b/backend/src/core/proxy-utils/producers/stash.js @@ -250,6 +250,10 @@ export default function Stash_Producer() { proxy['benchmark-url'] = proxy['test-url']; delete proxy['test-url']; } + if (proxy['test-timeout']) { + proxy['benchmark-timeout'] = proxy['test-timeout']; + delete proxy['test-timeout']; + } 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 71cb139..294b0a1 100644 --- a/backend/src/core/proxy-utils/producers/surge.js +++ b/backend/src/core/proxy-utils/producers/surge.js @@ -33,6 +33,8 @@ export default function Surge_Producer() { return wireguard_surge(proxy); case 'hysteria2': return hysteria2(proxy); + case 'ssh': + return ssh(proxy); } if (opts['include-unsupported-proxy'] && proxy.type === 'wireguard') { @@ -119,6 +121,18 @@ function shadowsocks(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -204,6 +218,18 @@ function trojan(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -278,6 +304,18 @@ function vmess(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -305,6 +343,61 @@ function vmess(proxy) { return result.toString(); } +function ssh(proxy) { + const result = new Result(proxy); + result.append(`${proxy.name}=ssh,${proxy.server},${proxy.port}`); + result.appendIfPresent(`,${proxy.username}`, 'username'); + result.appendIfPresent(`,${proxy.password}`, 'password'); + + result.appendIfPresent( + `,idle-timeout=${proxy['idle-timeout']}`, + 'idle-timeout', + ); + result.appendIfPresent( + `,server-fingerprint="${proxy['server-fingerprint']}"`, + 'server-fingerprint', + ); + + const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version']; + result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version'); + + result.appendIfPresent( + `,no-error-alert=${proxy['no-error-alert']}`, + 'no-error-alert', + ); + + // tfo + result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo'); + + // udp + result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); + + // test-url + result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); + + // block-quic + result.appendIfPresent(`,block-quic=${proxy['block-quic']}`, 'block-quic'); + + // underlying-proxy + result.appendIfPresent( + `,underlying-proxy=${proxy['underlying-proxy']}`, + 'underlying-proxy', + ); + + return result.toString(); +} function http(proxy) { const result = new Result(proxy); const type = proxy.tls ? 'https' : 'http'; @@ -341,6 +434,18 @@ function http(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -406,6 +511,18 @@ function socks5(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -469,6 +586,18 @@ function snell(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -547,6 +676,18 @@ function tuic(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -609,6 +750,18 @@ ${proxy.name}=wireguard`); // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -696,6 +849,18 @@ function wireguard_surge(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) { @@ -760,6 +925,18 @@ function hysteria2(proxy) { // test-url result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url'); + result.appendIfPresent( + `,test-timeout=${proxy['test-timeout']}`, + 'test-timeout', + ); + result.appendIfPresent(`,test-udp=${proxy['test-udp']}`, 'test-udp'); + result.appendIfPresent(`,hybrid=${proxy['hybrid']}`, 'hybrid'); + result.appendIfPresent(`,tos=${proxy['tos']}`, 'tos'); + result.appendIfPresent( + `,allow-other-interface=${proxy['allow-other-interface']}`, + 'allow-other-interface', + ); + result.appendIfPresent(`,interface=${proxy['interface']}`, 'interface'); // shadow-tls if (isPresent(proxy, 'shadow-tls-password')) {