From 90f5db2b7c375f8d0e974c8d2f7fb6304e110098 Mon Sep 17 00:00:00 2001 From: pompurin404 Date: Sun, 1 Sep 2024 20:43:03 +0800 Subject: [PATCH] try to set dns --- changelog.md | 4 ++ src/main/core/manager.ts | 98 +++++++++++++++++++++++++++++++--- src/main/index.ts | 4 +- src/renderer/src/pages/tun.tsx | 15 ++++++ src/shared/types.d.ts | 2 + 5 files changed, 114 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index b2469d6..0804133 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,10 @@ - 1.2.x YAML覆写语法有所变动,请更新后参考文档进行修改 +### New Features + +- MacOS 支持自动设置系统DNS + ### Performance Improvements - 优化规则/代理页面性能 diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index afb5faa..bdf289d 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -7,7 +7,12 @@ import { mihomoWorkDir } from '../utils/dirs' import { generateProfile } from './factory' -import { getAppConfig, patchAppConfig, patchControledMihomoConfig } from '../config' +import { + getAppConfig, + getControledMihomoConfig, + patchAppConfig, + patchControledMihomoConfig +} from '../config' import { dialog, safeStorage } from 'electron' import { pauseWebsockets, startMihomoTraffic } from './mihomoApi' import { writeFile } from 'fs/promises' @@ -18,12 +23,16 @@ let child: ChildProcess let retry = 10 export async function startCore(): Promise { - const { core = 'mihomo' } = await getAppConfig() + const { core = 'mihomo', autoSetDNS = true } = await getAppConfig() + const { tun } = await getControledMihomoConfig() const corePath = mihomoCorePath(core) await autoGrantCorePermition(corePath) await generateProfile() await checkProfile() - stopCore() + await stopCore() + if (tun?.enable && autoSetDNS) { + await setPublicDNS() + } child = spawn(corePath, ['-d', mihomoWorkDir()]) child.on('close', async (code, signal) => { await writeFile(logPath(), `[Manager]: Core closed, code: ${code}, signal: ${signal}\n`, { @@ -34,13 +43,13 @@ export async function startCore(): Promise { retry-- await restartCore() } else { - stopCore() + await stopCore() } }) return new Promise((resolve, reject) => { child.stdout?.on('data', async (data) => { if (data.toString().includes('updater: finished')) { - stopCore() + await stopCore() await startCore() } if (data.toString().includes('configure tun interface: operation not permitted')) { @@ -54,7 +63,7 @@ export async function startCore(): Promise { resolve(await startCore()) } else { dialog.showErrorBox('内核连接失败', '请尝试更改外部控制端口后重启内核') - stopCore() + await stopCore() reject('External controller listen error') } } @@ -69,7 +78,13 @@ export async function startCore(): Promise { }) } -export function stopCore(): void { +export async function stopCore(): Promise { + try { + await recoverDNS() + } catch (error) { + // todo + } + if (child) { child.removeAllListeners() child.kill('SIGINT') @@ -149,3 +164,72 @@ export async function manualGrantCorePermition(password?: string): Promise export function isEncryptionAvailable(): boolean { return safeStorage.isEncryptionAvailable() } + +async function getDefaultService(password?: string): Promise { + const execPromise = promisify(exec) + let sudo = '' + if (password) sudo = `echo "${password}" | sudo -S ` + const { stdout: deviceOut } = await execPromise(`${sudo}route -n get default`) + let device = deviceOut.split('\n').find((s) => s.includes('interface:')) + device = device?.trim().split(' ').slice(1).join(' ') + if (!device) throw new Error('Get device failed') + const { stdout: hardwareOut } = await execPromise(`${sudo}networksetup -listallhardwareports`) + const hardware = hardwareOut + .split('Ethernet Address:') + .find((s) => s.includes(`Device: ${device}`)) + if (!hardware) throw new Error('Get hardware failed') + for (const line of hardware.split('\n')) { + if (line.startsWith('Hardware Port:')) { + return line.trim().split(' ').slice(2).join(' ') + } + } + throw new Error('Get service failed') +} + +async function getOriginDNS(password?: string): Promise { + const execPromise = promisify(exec) + let sudo = '' + if (password) sudo = `echo "${password}" | sudo -S ` + const service = await getDefaultService(password) + const { stdout: dns } = await execPromise(`${sudo}networksetup -getdnsservers ${service}`) + if (dns.startsWith("There aren't any DNS Servers set on")) { + await patchAppConfig({ originDNS: 'Empty' }) + } else { + await patchAppConfig({ originDNS: dns.trim().replace(/\n/g, ' ') }) + } +} + +async function setDNS(dns: string, password?: string): Promise { + const service = await getDefaultService(password) + let sudo = '' + if (password) sudo = `echo "${password}" | sudo -S ` + const execPromise = promisify(exec) + await execPromise(`${sudo}networksetup -setdnsservers ${service} ${dns}`) + // todo +} + +async function setPublicDNS(): Promise { + if (process.platform !== 'darwin') return + const { originDNS, encryptedPassword } = await getAppConfig() + if (!originDNS) { + let password: string | undefined + if (encryptedPassword && isEncryptionAvailable()) { + password = safeStorage.decryptString(Buffer.from(encryptedPassword)) + } + await getOriginDNS(password) + await setDNS('223.5.5.5', password) + } +} + +async function recoverDNS(): Promise { + if (process.platform !== 'darwin') return + const { originDNS, encryptedPassword } = await getAppConfig() + if (originDNS) { + let password: string | undefined + if (encryptedPassword && isEncryptionAvailable()) { + password = safeStorage.decryptString(Buffer.from(encryptedPassword)) + } + await setDNS(originDNS, password) + await patchAppConfig({ originDNS: undefined }) + } +} diff --git a/src/main/index.ts b/src/main/index.ts index cd3dd8c..c4b3557 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -68,9 +68,9 @@ app.on('window-all-closed', (e) => { // } }) -app.on('before-quit', () => { +app.on('before-quit', async () => { pauseWebsockets() - stopCore() + await stopCore() triggerSysProxy(false) app.exit() }) diff --git a/src/renderer/src/pages/tun.tsx b/src/renderer/src/pages/tun.tsx index 0b46d07..5fdf8c0 100644 --- a/src/renderer/src/pages/tun.tsx +++ b/src/renderer/src/pages/tun.tsx @@ -7,9 +7,12 @@ import { manualGrantCorePermition, restartCore, setupFirewall } from '@renderer/ import { platform } from '@renderer/utils/init' import React, { Key, useState } from 'react' import BasePasswordModal from '@renderer/components/base/base-password-modal' +import { useAppConfig } from '@renderer/hooks/use-app-config' const Tun: React.FC = () => { const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() + const { appConfig, patchAppConfig } = useAppConfig() + const { autoSetDNS = true } = appConfig || {} const { tun } = controledMihomoConfig || {} const [loading, setLoading] = useState(false) const [openPasswordModal, setOpenPasswordModal] = useState(false) @@ -137,6 +140,18 @@ const Tun: React.FC = () => { )} + {platform === 'darwin' && ( + + { + await patchAppConfig({ autoSetDNS: v }) + }} + /> + + )} +