diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index f0c16e57..8e4f75cc 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -39,7 +39,10 @@ impl IClashTemp { tun.insert("auto-detect-interface".into(), true.into()); tun.insert("dns-hijack".into(), vec!["any:53"].into()); tun.insert("mtu".into(), 9000.into()); - + #[cfg(not(target_os = "windows"))] + map.insert("redir-port".into(), 7895.into()); + #[cfg(target_os = "linux")] + map.insert("tproxy-port".into(), 7896.into()); map.insert("mixed-port".into(), 7897.into()); map.insert("socks-port".into(), 7898.into()); map.insert("port".into(), 7899.into()); @@ -54,11 +57,18 @@ impl IClashTemp { } fn guard(mut config: Mapping) -> Mapping { + #[cfg(not(target_os = "windows"))] + let redir_port = Self::guard_redir_port(&config); + #[cfg(target_os = "linux")] + let tproxy_port = Self::guard_tproxy_port(&config); let mixed_port = Self::guard_mixed_port(&config); let socks_port = Self::guard_socks_port(&config); let port = Self::guard_port(&config); let ctrl = Self::guard_server_ctrl(&config); - + #[cfg(not(target_os = "windows"))] + config.insert("redir-port".into(), redir_port.into()); + #[cfg(target_os = "linux")] + config.insert("tproxy-port".into(), tproxy_port.into()); config.insert("mixed-port".into(), mixed_port.into()); config.insert("socks-port".into(), socks_port.into()); config.insert("port".into(), port.into()); @@ -110,6 +120,37 @@ impl IClashTemp { }), } } + #[cfg(not(target_os = "windows"))] + pub fn guard_redir_port(config: &Mapping) -> u16 { + let mut port = config + .get("redir-port") + .and_then(|value| match value { + Value::String(val_str) => val_str.parse().ok(), + Value::Number(val_num) => val_num.as_u64().map(|u| u as u16), + _ => None, + }) + .unwrap_or(7895); + if port == 0 { + port = 7895; + } + port + } + + #[cfg(target_os = "linux")] + pub fn guard_tproxy_port(config: &Mapping) -> u16 { + let mut port = config + .get("tproxy-port") + .and_then(|value| match value { + Value::String(val_str) => val_str.parse().ok(), + Value::Number(val_num) => val_num.as_u64().map(|u| u as u16), + _ => None, + }) + .unwrap_or(7896); + if port == 0 { + port = 7896; + } + port + } pub fn guard_mixed_port(config: &Mapping) -> u16 { let mut port = config diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index b9784250..cba0d3e7 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -126,7 +126,13 @@ pub struct IVerge { /// 是否启用随机端口 pub enable_random_port: Option, - /// verge mixed port 用于覆盖 clash 的 mixed port + /// verge 的各种 port 用于覆盖 clash 的各种 port + #[cfg(not(target_os = "windows"))] + pub verge_redir_port: Option, + + #[cfg(target_os = "linux")] + pub verge_tproxy_port: Option, + pub verge_mixed_port: Option, pub verge_socks_port: Option, @@ -190,6 +196,10 @@ impl IVerge { enable_silent_start: Some(false), enable_system_proxy: Some(false), enable_random_port: Some(false), + #[cfg(not(target_os = "windows"))] + verge_redir_port: Some(7895), + #[cfg(target_os = "linux")] + verge_tproxy_port: Some(7896), verge_mixed_port: Some(7897), verge_socks_port: Some(7898), verge_port: Some(7899), @@ -239,6 +249,10 @@ impl IVerge { patch!(enable_auto_launch); patch!(enable_silent_start); patch!(enable_random_port); + #[cfg(not(target_os = "windows"))] + patch!(verge_redir_port); + #[cfg(target_os = "linux")] + patch!(verge_tproxy_port); patch!(verge_mixed_port); patch!(verge_socks_port); patch!(verge_port); diff --git a/src-tauri/src/enhance/field.rs b/src-tauri/src/enhance/field.rs index 3a4b7fcf..0f84f9f2 100644 --- a/src-tauri/src/enhance/field.rs +++ b/src-tauri/src/enhance/field.rs @@ -1,8 +1,10 @@ use serde_yaml::{Mapping, Value}; use std::collections::HashSet; -pub const HANDLE_FIELDS: [&str; 9] = [ +pub const HANDLE_FIELDS: [&str; 11] = [ "mode", + "redir-port", + "tproxy-port", "mixed-port", "socks-port", "port", diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index a4d0d81f..5fa9a502 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -107,6 +107,8 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { Config::clash().draft().patch_config(patch.clone()); match { + let redir_port = patch.get("redir-port"); + let tproxy_port = patch.get("tproxy-port"); let mixed_port = patch.get("mixed-port"); let socks_port = patch.get("socks-port"); let port = patch.get("port"); @@ -129,7 +131,9 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { }; // 激活订阅 - if mixed_port.is_some() + if redir_port.is_some() + || tproxy_port.is_some() + || mixed_port.is_some() || socks_port.is_some() || port.is_some() || patch.get("secret").is_some() diff --git a/src/components/setting/mods/clash-port-viewer.tsx b/src/components/setting/mods/clash-port-viewer.tsx index 5c345210..949cef11 100644 --- a/src/components/setting/mods/clash-port-viewer.tsx +++ b/src/components/setting/mods/clash-port-viewer.tsx @@ -5,6 +5,8 @@ import { List, ListItem, ListItemText, TextField } from "@mui/material"; import { useClashInfo } from "@/hooks/use-clash"; import { BaseDialog, DialogRef, Notice } from "@/components/base"; import { useVerge } from "@/hooks/use-verge"; +import getSystem from "@/utils/get-system"; +const OS = getSystem(); export const ClashPortViewer = forwardRef((props, ref) => { const { t } = useTranslation(); @@ -13,6 +15,12 @@ export const ClashPortViewer = forwardRef((props, ref) => { const { verge, patchVerge } = useVerge(); const [open, setOpen] = useState(false); + const [redirPort, setRedirPort] = useState( + verge?.verge_redir_port ?? clashInfo?.redir_port ?? 7895 + ); + const [tproxyPort, setTproxyPort] = useState( + verge?.verge_tproxy_port ?? clashInfo?.tproxy_port ?? 7896 + ); const [mixedPort, setMixedPort] = useState( verge?.verge_mixed_port ?? clashInfo?.mixed_port ?? 7897 ); @@ -25,6 +33,8 @@ export const ClashPortViewer = forwardRef((props, ref) => { useImperativeHandle(ref, () => ({ open: () => { + if (verge?.verge_redir_port) setRedirPort(verge?.verge_redir_port); + if (verge?.verge_tproxy_port) setTproxyPort(verge?.verge_tproxy_port); if (verge?.verge_mixed_port) setMixedPort(verge?.verge_mixed_port); if (verge?.verge_socks_port) setSocksPort(verge?.verge_socks_port); if (verge?.verge_port) setPort(verge?.verge_port); @@ -35,6 +45,8 @@ export const ClashPortViewer = forwardRef((props, ref) => { const onSave = useLockFn(async () => { if ( + redirPort === verge?.verge_redir_port && + tproxyPort === verge?.verge_tproxy_port && mixedPort === verge?.verge_mixed_port && socksPort === verge?.verge_socks_port && port === verge?.verge_port @@ -42,11 +54,22 @@ export const ClashPortViewer = forwardRef((props, ref) => { setOpen(false); return; } - if (mixedPort === socksPort || mixedPort === port || socksPort === port) { + + if ( + new Set([redirPort, tproxyPort, mixedPort, socksPort, port]).size !== 5 + ) { Notice.error("Port conflict!", 4000); return; } try { + if (OS !== "windows") { + await patchInfo({ "redir-port": redirPort }); + await patchVerge({ verge_redir_port: redirPort }); + } + if (OS === "linux") { + await patchInfo({ "tproxy-port": tproxyPort }); + await patchVerge({ verge_tproxy_port: tproxyPort }); + } await patchInfo({ "mixed-port": mixedPort }); await patchInfo({ "socks-port": socksPort }); await patchInfo({ port }); @@ -72,6 +95,35 @@ export const ClashPortViewer = forwardRef((props, ref) => { onOk={onSave} > + {OS !== "windows" && ( + + + + setRedirPort(+e.target.value?.replace(/\D+/, "").slice(0, 5)) + } + /> + + )} + {OS === "linux" && ( + + + + setTproxyPort(+e.target.value?.replace(/\D+/, "").slice(0, 5)) + } + /> + + )} + { patch: Partial< Pick< IConfigData, - "port" | "socks-port" | "mixed-port" | "external-controller" | "secret" + | "port" + | "socks-port" + | "mixed-port" + | "redir-port" + | "tproxy-port" + | "external-controller" + | "secret" > > ) => { const hasInfo = + patch["redir-port"] != null || + patch["tproxy-port"] != null || patch["mixed-port"] != null || patch["socks-port"] != null || patch["port"] != null || @@ -62,6 +70,26 @@ export const useClashInfo = () => { if (!hasInfo) return; + if (patch["redir-port"]) { + const port = patch["redir-port"]; + if (port < 1000) { + throw new Error("The port should not < 1000"); + } + if (port > 65536) { + throw new Error("The port should not > 65536"); + } + } + + if (patch["tproxy-port"]) { + const port = patch["tproxy-port"]; + if (port < 1000) { + throw new Error("The port should not < 1000"); + } + if (port > 65536) { + throw new Error("The port should not > 65536"); + } + } + if (patch["mixed-port"]) { const port = patch["mixed-port"]; if (port < 1000) { diff --git a/src/services/types.d.ts b/src/services/types.d.ts index 94caf777..31462643 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -142,6 +142,8 @@ interface IClashInfo { // status: string; mixed_port?: number; // clash mixed port socks_port?: number; // clash socks port + redir_port?: number; // clash redir port + tproxy_port?: number; // clash tproxy port port?: number; // clash http port server?: string; // external-controller secret?: string; @@ -214,6 +216,8 @@ interface IVergeConfig { enable_random_port?: boolean; verge_mixed_port?: number; verge_socks_port?: number; + verge_redir_port?: number; + verge_tproxy_port?: number; verge_port?: number; enable_proxy_guard?: boolean; proxy_guard_duration?: number;