diff --git a/scripts/check.mjs b/scripts/check.mjs index bf75cba..a2e4cd7 100644 --- a/scripts/check.mjs +++ b/scripts/check.mjs @@ -279,27 +279,50 @@ async function resolveSidecar(binInfo) { } } +const resolveSetDnsScript = () => + resolveResource({ + file: "set_dns.sh", + localPath: path.join(cwd, "scripts/set_dns.sh"), + }); +const resolveUnSetDnsScript = () => + resolveResource({ + file: "unset_dns.sh", + localPath: path.join(cwd, "scripts/unset_dns.sh"), + }); + /** * download the file to the resources dir */ async function resolveResource(binInfo) { - const { file, downloadURL } = binInfo; + const { file, downloadURL, localPath } = binInfo; const resDir = path.join(cwd, "src-tauri/resources"); const targetPath = path.join(resDir, file); if (!FORCE && fs.existsSync(targetPath)) return; - await fsp.mkdir(resDir, { recursive: true }); - await downloadFile(downloadURL, targetPath); + if (downloadURL) { + await fsp.mkdir(resDir, { recursive: true }); + await downloadFile(downloadURL, targetPath); + } + + if (localPath) { + await fs.copyFile(localPath, targetPath, (err) => { + if (err) { + console.error("Error copying file:", err); + } else { + console.log("File was copied successfully"); + } + }); + log_debug(`copy file finished: "${localPath}"`); + } log_success(`${file} finished`); } /** * download file and save to `path` - */ -async function downloadFile(url, path) { + */ async function downloadFile(url, path) { const options = {}; const httpProxy = @@ -474,6 +497,18 @@ const tasks = [ retry: 5, winOnly: true, }, + { + name: "set_dns_script", + func: resolveSetDnsScript, + retry: 5, + macosOnly: true, + }, + { + name: "unset_dns_script", + func: resolveUnSetDnsScript, + retry: 5, + macosOnly: true, + }, ]; async function runTask() { @@ -482,6 +517,7 @@ async function runTask() { if (task.winOnly && platform !== "win32") return runTask(); if (task.linuxOnly && platform !== "linux") return runTask(); if (task.unixOnly && platform === "win32") return runTask(); + if (task.macosOnly && platform !== "darwin") return runTask(); for (let i = 0; i < task.retry; i++) { try { diff --git a/scripts/set_dns.sh b/scripts/set_dns.sh new file mode 100644 index 0000000..011adf7 --- /dev/null +++ b/scripts/set_dns.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +function is_valid_ip() { + local ip=$1 + local IFS='.' + local -a octets + + if [[ ! $ip =~ ^([0-9]+\.){3}[0-9]+$ ]]; then + return 1 + fi + + read -r -a octets <<<"$ip" + + if [ "${#octets[@]}" -ne 4 ]; then + return 1 + fi + + for octet in "${octets[@]}"; do + if ! [[ "$octet" =~ ^[0-9]+$ ]] || ((octet < 0 || octet > 255)); then + return 1 + fi + done + + return 0 +} + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +if ! is_valid_ip "$1"; then + echo "$1 is not a valid IP address." + exit 1 +fi + +nic=$(route -n get default | grep "interface" | awk '{print $2}') +hardware_port=$(networksetup -listallhardwareports | awk -v dev="$nic" ' + /Hardware Port:/{ + port=$0; gsub("Hardware Port: ", "", port) + } + /Device: /{ + if ($2 == dev) { + print port; + exit + } + } +') + +original_dns=$(networksetup -getdnsservers "$hardware_port") + +if [ ${#original_dns} -gt 15 ]; then + echo "Empty" >original_dns.txt +else + echo $original_dns >original_dns.txt +fi + +networksetup -setdnsservers "$hardware_port" "$1" diff --git a/scripts/unset_dns.sh b/scripts/unset_dns.sh new file mode 100644 index 0000000..9d98aff --- /dev/null +++ b/scripts/unset_dns.sh @@ -0,0 +1,22 @@ +#!/bin/bash +nic=$(route -n get default | grep "interface" | awk '{print $2}') + +hardware_port=$(networksetup -listallhardwareports | awk -v dev="$nic" ' + /Hardware Port:/{ + port=$0; gsub("Hardware Port: ", "", port) + } + /Device: /{ + if ($2 == dev) { + print port; + exit + } + } +') + +if [ -f original_dns.txt ]; then + original_dns=$(cat original_dns.txt) +else + original_dns=$(networksetup -getdnsservers "$hardware_port") +fi + +networksetup -setdnsservers "$hardware_port" $original_dns diff --git a/src-tauri/src/enhance/tun.rs b/src-tauri/src/enhance/tun.rs index 9e7446d..87c78ef 100644 --- a/src-tauri/src/enhance/tun.rs +++ b/src-tauri/src/enhance/tun.rs @@ -35,8 +35,12 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping { revise!(dns_val, "ip-v6", true); revise!(dns_val, "enhanced-mode", "fake-ip"); revise!(dns_val, "fake-ip-range", "10.96.0.0/16"); + #[cfg(target_os = "macos")] + set_public_dns("10.96.0.2".to_string()).await; } else { revise!(dns_val, "enhanced-mode", "redir-host"); + #[cfg(target_os = "macos")] + restore_public_dns().await; } revise!(tun_val, "enable", enable); @@ -44,3 +48,76 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping { revise!(config, "dns", dns_val); config } + +#[cfg(target_os = "macos")] +async fn set_public_dns(dns_server: String) { + use crate::core::handle; + use crate::utils::dirs; + use tauri_plugin_shell::ShellExt; + let app_handle = handle::Handle::global().app_handle().unwrap(); + + log::info!(target: "app", "try to set system dns"); + let resource_dir = dirs::app_resources_dir().unwrap(); + let script = resource_dir.join("set_dns.sh"); + if !script.exists() { + log::error!(target: "app", "set_dns.sh not found"); + return; + } + let script = script.to_string_lossy().into_owned(); + match app_handle + .shell() + .command("bash") + .args([script, dns_server]) + .current_dir(resource_dir) + .status() + .await + { + Ok(status) => { + if status.success() { + log::info!(target: "app", "set system dns successfully"); + } else { + let code = status.code().unwrap_or(-1); + log::error!(target: "app", "set system dns failed: {code}"); + } + } + Err(err) => { + log::error!(target: "app", "set system dns failed: {err}"); + } + } +} + +#[cfg(target_os = "macos")] +async fn restore_public_dns() { + use crate::core::handle; + use crate::utils::dirs; + use tauri_plugin_shell::ShellExt; + let app_handle = handle::Handle::global().app_handle().unwrap(); + log::info!(target: "app", "try to unset system dns"); + let resource_dir = dirs::app_resources_dir().unwrap(); + let script = resource_dir.join("unset_dns.sh"); + if !script.exists() { + log::error!(target: "app", "unset_dns.sh not found"); + return; + } + let script = script.to_string_lossy().into_owned(); + match app_handle + .shell() + .command("bash") + .args([script]) + .current_dir(resource_dir) + .status() + .await + { + Ok(status) => { + if status.success() { + log::info!(target: "app", "unset system dns successfully"); + } else { + let code = status.code().unwrap_or(-1); + log::error!(target: "app", "unset system dns failed: {code}"); + } + } + Err(err) => { + log::error!(target: "app", "unset system dns failed: {err}"); + } + } +} diff --git a/src-tauri/tauri.macos.conf.json b/src-tauri/tauri.macos.conf.json index 95dc299..09dcdd1 100644 --- a/src-tauri/tauri.macos.conf.json +++ b/src-tauri/tauri.macos.conf.json @@ -3,6 +3,7 @@ "identifier": "io.github.clash-verge-rev.clash-verge-rev", "bundle": { "targets": ["app", "dmg"], + "resources": ["resources/set_dns.sh", "resources/unset_dns.sh"], "macOS": { "frameworks": [], "minimumSystemVersion": "10.15",