feat: add authenticated service IPC communication

This commit is contained in:
wonfen
2025-05-08 00:14:58 +08:00
parent dc798fe2dd
commit 7370f00857
6 changed files with 1082 additions and 121 deletions

15
src-tauri/Cargo.lock generated
View File

@@ -1058,6 +1058,8 @@ dependencies = [
"futures", "futures",
"gethostname 1.0.2", "gethostname 1.0.2",
"getrandom 0.3.2", "getrandom 0.3.2",
"hex",
"hmac",
"image", "image",
"imageproc", "imageproc",
"lazy_static", "lazy_static",
@@ -1079,6 +1081,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"sha2 0.10.9",
"sys-locale", "sys-locale",
"sysinfo", "sysinfo",
"sysproxy", "sysproxy",
@@ -1712,7 +1715,7 @@ dependencies = [
"hex", "hex",
"md-5", "md-5",
"rand 0.8.5", "rand 0.8.5",
"sha2 0.10.8", "sha2 0.10.9",
] ]
[[package]] [[package]]
@@ -4874,7 +4877,7 @@ checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"pest", "pest",
"sha2 0.10.8", "sha2 0.10.9",
] ]
[[package]] [[package]]
@@ -6370,9 +6373,9 @@ dependencies = [
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.8" version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
@@ -6931,7 +6934,7 @@ dependencies = [
"semver 1.0.26", "semver 1.0.26",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.8", "sha2 0.10.9",
"syn 2.0.100", "syn 2.0.100",
"tauri-utils", "tauri-utils",
"thiserror 2.0.12", "thiserror 2.0.12",
@@ -9231,7 +9234,7 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"raw-window-handle", "raw-window-handle",
"sha2 0.10.8", "sha2 0.10.9",
"soup3", "soup3",
"tao-macros", "tao-macros",
"thiserror 2.0.12", "thiserror 2.0.12",

View File

@@ -77,6 +77,9 @@ ab_glyph = "0.2.29"
tungstenite = "0.26.2" tungstenite = "0.26.2"
libc = "0.2.172" libc = "0.2.172"
gethostname = "1.0.2" gethostname = "1.0.2"
hmac = "0.12.1"
sha2 = "0.10.9"
hex = "0.4.3"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
runas = "=1.2.0" runas = "=1.2.0"

View File

@@ -4,6 +4,7 @@ mod core;
pub mod handle; pub mod handle;
pub mod hotkey; pub mod hotkey;
pub mod service; pub mod service;
pub mod service_ipc;
pub mod sysopt; pub mod sysopt;
pub mod timer; pub mod timer;
pub mod tray; pub mod tray;

View File

@@ -1,22 +1,19 @@
use crate::{ use crate::{
config::Config, config::Config,
core::service_ipc::{send_ipc_request, IpcCommand},
logging, logging,
utils::{dirs, logging::Type}, utils::{dirs, logging::Type},
}; };
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashMap,
env::current_exe, env::current_exe,
path::PathBuf, path::PathBuf,
process::Command as StdCommand, process::Command as StdCommand,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
// Windows only const REQUIRED_SERVICE_VERSION: &str = "1.0.7"; // 定义所需的服务版本号
const SERVICE_URL: &str = "http://127.0.0.1:33211";
const REQUIRED_SERVICE_VERSION: &str = "1.0.6"; // 定义所需的服务版本号
// 限制重装时间和次数的常量 // 限制重装时间和次数的常量
const REINSTALL_COOLDOWN_SECS: u64 = 300; // 5分钟冷却期 const REINSTALL_COOLDOWN_SECS: u64 = 300; // 5分钟冷却期
@@ -91,6 +88,7 @@ impl ServiceState {
} }
} }
// 保留核心数据结构但将HTTP特定的结构体合并为通用结构体
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct ResponseBody { pub struct ResponseBody {
pub core_type: Option<String>, pub core_type: Option<String>,
@@ -105,6 +103,7 @@ pub struct VersionResponse {
pub version: String, pub version: String,
} }
// 保留通用的响应结构体用于IPC通信后的数据解析
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct JsonResponse { pub struct JsonResponse {
pub code: u64, pub code: u64,
@@ -112,13 +111,6 @@ pub struct JsonResponse {
pub data: Option<ResponseBody>, pub data: Option<ResponseBody>,
} }
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct VersionJsonResponse {
pub code: u64,
pub msg: String,
pub data: Option<VersionResponse>,
}
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub async fn uninstall_service() -> Result<()> { pub async fn uninstall_service() -> Result<()> {
logging!(info, Type::Service, true, "uninstall service"); logging!(info, Type::Service, true, "uninstall service");
@@ -480,97 +472,272 @@ pub async fn reinstall_service() -> Result<()> {
} }
} }
/// check the windows service status /// 检查服务状态 - 使用IPC通信
pub async fn check_service() -> Result<JsonResponse> { pub async fn check_service() -> Result<JsonResponse> {
use crate::utils::network::{NetworkManager, ProxyType}; logging!(info, Type::Service, true, "开始检查服务状态 (IPC)");
let url = format!("{SERVICE_URL}/get_clash"); // 使用IPC通信
let payload = serde_json::json!({});
logging!(debug, Type::Service, true, "发送GetClash请求");
// 使用无代理模式和3秒超时检查服务 match send_ipc_request(IpcCommand::GetClash, payload).await {
let response = NetworkManager::global() Ok(response) => {
.get(&url, ProxyType::NoProxy, Some(3), None, false) logging!(
.await debug,
.context("failed to connect to the Clash Verge Service")? Type::Service,
.json::<JsonResponse>() true,
.await "收到GetClash响应: success={}, error={:?}",
.context("failed to parse the Clash Verge Service response")?; response.success,
response.error
);
Ok(response) if !response.success {
} let err_msg = response.error.unwrap_or_else(|| "未知服务错误".to_string());
logging!(error, Type::Service, true, "服务响应错误: {}", err_msg);
bail!(err_msg);
}
/// check the service version match response.data {
pub async fn check_service_version() -> Result<String> { Some(data) => {
use crate::utils::network::{NetworkManager, ProxyType}; // 检查嵌套结构
if let (Some(code), Some(msg)) = (data.get("code"), data.get("msg")) {
let code_value = code.as_u64().unwrap_or(0);
let msg_value = msg.as_str().unwrap_or("ok").to_string();
let url = format!("{SERVICE_URL}/version"); // 提取嵌套的data字段并解析为ResponseBody
let response_body = if let Some(nested_data) = data.get("data") {
match serde_json::from_value::<ResponseBody>(nested_data.clone()) {
Ok(body) => Some(body),
Err(e) => {
logging!(
warn,
Type::Service,
true,
"解析嵌套的ResponseBody失败: {}; 尝试其他方式",
e
);
None
}
}
} else {
None
};
// 使用无代理模式和3秒超时检查服务版本 let json_response = JsonResponse {
let response = NetworkManager::global() code: code_value,
.get(&url, ProxyType::NoProxy, Some(3), None, false) msg: msg_value,
.await data: response_body,
.context("failed to connect to the Clash Verge Service")? };
.json::<VersionJsonResponse>()
.await
.context("failed to parse the Clash Verge Service version response")?;
match response.data { logging!(
Some(data) => Ok(data.version), info,
None => bail!("service version not found in response"), Type::Service,
true,
"服务检测成功: code={}, msg={}, data存在={}",
json_response.code,
json_response.msg,
json_response.data.is_some()
);
return Ok(json_response);
} else {
// 尝试直接解析
match serde_json::from_value::<JsonResponse>(data.clone()) {
Ok(json_response) => {
logging!(
info,
Type::Service,
true,
"服务检测成功: code={}, msg={}",
json_response.code,
json_response.msg
);
return Ok(json_response);
}
Err(e) => {
logging!(
error,
Type::Service,
true,
"解析服务响应失败: {}; 原始数据: {:?}",
e,
data
);
bail!("无法解析服务响应数据: {}", e)
}
}
}
}
None => {
logging!(error, Type::Service, true, "服务响应中没有数据");
bail!("服务响应中没有数据")
}
}
}
Err(e) => {
logging!(error, Type::Service, true, "IPC通信失败: {}", e);
bail!("无法连接到Clash Verge Service: {}", e)
}
} }
} }
/// check if service needs to be reinstalled /// 检查服务版本 - 使用IPC通信
pub async fn check_service_version() -> Result<String> {
logging!(info, Type::Service, true, "开始检查服务版本 (IPC)");
let payload = serde_json::json!({});
logging!(debug, Type::Service, true, "发送GetVersion请求");
match send_ipc_request(IpcCommand::GetVersion, payload).await {
Ok(response) => {
logging!(
debug,
Type::Service,
true,
"收到GetVersion响应: success={}, error={:?}",
response.success,
response.error
);
if !response.success {
let err_msg = response
.error
.unwrap_or_else(|| "获取服务版本失败".to_string());
logging!(error, Type::Service, true, "获取版本错误: {}", err_msg);
bail!(err_msg);
}
match response.data {
Some(data) => {
if let Some(nested_data) = data.get("data") {
if let Some(version) = nested_data.get("version") {
if let Some(version_str) = version.as_str() {
logging!(
info,
Type::Service,
true,
"获取到服务版本: {}",
version_str
);
return Ok(version_str.to_string());
}
}
logging!(
error,
Type::Service,
true,
"嵌套数据中没有version字段: {:?}",
nested_data
);
} else {
// 兼容旧格式
match serde_json::from_value::<VersionResponse>(data.clone()) {
Ok(version_response) => {
logging!(
info,
Type::Service,
true,
"获取到服务版本: {}",
version_response.version
);
return Ok(version_response.version);
}
Err(e) => {
logging!(
error,
Type::Service,
true,
"解析版本响应失败: {}; 原始数据: {:?}",
e,
data
);
bail!("无法解析服务版本数据: {}", e)
}
}
}
bail!("响应中未找到有效的版本信息")
}
None => {
logging!(error, Type::Service, true, "版本响应中没有数据");
bail!("服务版本响应中没有数据")
}
}
}
Err(e) => {
logging!(error, Type::Service, true, "IPC通信失败: {}", e);
bail!("无法连接到Clash Verge Service: {}", e)
}
}
}
/// 检查服务是否需要重装
pub async fn check_service_needs_reinstall() -> bool { pub async fn check_service_needs_reinstall() -> bool {
// 获取当前服务状态 logging!(info, Type::Service, true, "开始检查服务是否需要重装");
let service_state = ServiceState::get(); let service_state = ServiceState::get();
// 首先检查是否在冷却期或超过重装次数限制
if !service_state.can_reinstall() { if !service_state.can_reinstall() {
log::info!(target: "app", "service reinstall check: in cooldown period or max attempts reached"); log::info!(target: "app", "服务重装检查: 处于冷却期或已达最大尝试次数");
return false; return false;
} }
// 然后才检查版本和可用性 // 检查版本和可用性
match check_service_version().await { match check_service_version().await {
Ok(version) => { Ok(version) => {
// 打印更详细的日志,方便排查问题
log::info!(target: "app", "服务版本检测:当前={}, 要求={}", version, REQUIRED_SERVICE_VERSION); log::info!(target: "app", "服务版本检测:当前={}, 要求={}", version, REQUIRED_SERVICE_VERSION);
logging!(
info,
Type::Service,
true,
"服务版本检测:当前={}, 要求={}",
version,
REQUIRED_SERVICE_VERSION
);
let needs_reinstall = version != REQUIRED_SERVICE_VERSION; let needs_reinstall = version != REQUIRED_SERVICE_VERSION;
if needs_reinstall { if needs_reinstall {
log::warn!(target: "app", "发现服务版本不匹配,需要重装! 当前={}, 要求={}", log::warn!(target: "app", "发现服务版本不匹配,需要重装! 当前={}, 要求={}",
version, REQUIRED_SERVICE_VERSION); version, REQUIRED_SERVICE_VERSION);
logging!(warn, Type::Service, true, "服务版本不匹配,需要重装");
// 打印版本字符串的原始字节,确认没有隐藏字符
log::debug!(target: "app", "当前版本字节: {:?}", version.as_bytes()); log::debug!(target: "app", "当前版本字节: {:?}", version.as_bytes());
log::debug!(target: "app", "要求版本字节: {:?}", REQUIRED_SERVICE_VERSION.as_bytes()); log::debug!(target: "app", "要求版本字节: {:?}", REQUIRED_SERVICE_VERSION.as_bytes());
} else { } else {
log::info!(target: "app", "服务版本匹配,无需重装"); log::info!(target: "app", "服务版本匹配,无需重装");
logging!(info, Type::Service, true, "服务版本匹配,无需重装");
} }
needs_reinstall needs_reinstall
} }
Err(err) => { Err(err) => {
// 检查服务是否可用如果可用但版本检查失败可能只是版本API有问题 logging!(error, Type::Service, true, "检查服务版本失败: {}", err);
// 检查服务是否可用
match is_service_running().await { match is_service_running().await {
Ok(true) => { Ok(true) => {
log::info!(target: "app", "service is running but version check failed: {}", err); log::info!(target: "app", "服务正在运行但版本检查失败: {}", err);
false // 服务在运行,不需要重装 logging!(
info,
Type::Service,
true,
"服务正在运行但版本检查失败: {}",
err
);
false
} }
_ => { _ => {
log::info!(target: "app", "service is not running or unavailable"); log::info!(target: "app", "服务不可用或未运行,需要重装");
true // 服务不可用,需要重装 logging!(info, Type::Service, true, "服务不可用或未运行,需要重装");
true
} }
} }
} }
} }
} }
/// 尝试使用现有服务启动核心,不进行重装 /// 尝试使用服务启动core
pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result<()> { pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result<()> {
use crate::utils::network::{NetworkManager, ProxyType}; log::info!(target:"app", "尝试使用现有服务启动核心 (IPC)");
logging!(info, Type::Service, true, "尝试使用现有服务启动核心");
log::info!(target:"app", "attempting to start core with existing service");
let clash_core = { Config::verge().latest().clash_core.clone() }; let clash_core = { Config::verge().latest().clash_core.clone() };
let clash_core = clash_core.unwrap_or("verge-mihomo".into()); let clash_core = clash_core.unwrap_or("verge-mihomo".into());
@@ -588,30 +755,70 @@ pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result
let config_file = dirs::path_to_str(config_file)?; let config_file = dirs::path_to_str(config_file)?;
let mut map = HashMap::new(); // 构建启动参数
map.insert("core_type", clash_core.as_str()); let payload = serde_json::json!({
map.insert("bin_path", bin_path); "core_type": clash_core,
map.insert("config_dir", config_dir); "bin_path": bin_path,
map.insert("config_file", config_file); "config_dir": config_dir,
map.insert("log_file", log_path); "config_file": config_file,
"log_file": log_path,
});
log::info!(target:"app", "start service: {:?}", map.clone()); log::info!(target:"app", "启动服务参数: {:?}", payload);
logging!(info, Type::Service, true, "发送StartClash请求");
let url = format!("{SERVICE_URL}/start_clash"); // 使用IPC通信
match send_ipc_request(IpcCommand::StartClash, payload).await {
Ok(response) => {
logging!(
info,
Type::Service,
true,
"收到StartClash响应: success={}, error={:?}",
response.success,
response.error
);
// 使用网络管理器发送POST请求 if !response.success {
let client = NetworkManager::global().get_client(ProxyType::NoProxy); let err_msg = response.error.unwrap_or_else(|| "启动核心失败".to_string());
let _ = client logging!(error, Type::Service, true, "启动核心失败: {}", err_msg);
.post(url) bail!(err_msg);
.json(&map) }
.send()
.await
.context("failed to connect to the Clash Verge Service")?;
Ok(()) // 添加对嵌套JSON结构的处理
if let Some(data) = &response.data {
if let Some(code) = data.get("code") {
let code_value = code.as_u64().unwrap_or(1);
let msg = data
.get("msg")
.and_then(|m| m.as_str())
.unwrap_or("未知错误");
if code_value != 0 {
logging!(
error,
Type::Service,
true,
"启动核心返回错误: code={}, msg={}",
code_value,
msg
);
bail!("启动核心失败: {}", msg);
}
}
}
logging!(info, Type::Service, true, "服务成功启动核心");
Ok(())
}
Err(e) => {
logging!(error, Type::Service, true, "启动核心IPC通信失败: {}", e);
bail!("无法连接到Clash Verge Service: {}", e)
}
}
} }
/// start the clash by service // 以服务启动core
pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> { pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
log::info!(target: "app", "正在尝试通过服务启动核心"); log::info!(target: "app", "正在尝试通过服务启动核心");
@@ -621,39 +828,33 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
log::info!(target: "app", "检测到服务版本: {}, 要求版本: {}", log::info!(target: "app", "检测到服务版本: {}, 要求版本: {}",
version, REQUIRED_SERVICE_VERSION); version, REQUIRED_SERVICE_VERSION);
// 通过字节比较确保完全匹配
if version.as_bytes() != REQUIRED_SERVICE_VERSION.as_bytes() { if version.as_bytes() != REQUIRED_SERVICE_VERSION.as_bytes() {
log::warn!(target: "app", "服务版本不匹配,需要重装"); log::warn!(target: "app", "服务版本不匹配,需要重装");
false // 版本不匹配 false
} else { } else {
log::info!(target: "app", "服务版本匹配"); log::info!(target: "app", "服务版本匹配");
true // 版本匹配 true
} }
} }
Err(err) => { Err(err) => {
log::warn!(target: "app", "无法获取服务版本: {}", err); log::warn!(target: "app", "无法获取服务版本: {}", err);
false // 无法获取版本 false
} }
}; };
// 先尝试直接启动服务,如果服务可用且版本匹配
if version_check { if version_check {
if let Ok(true) = is_service_running().await { if let Ok(true) = is_service_running().await {
// 服务正在运行且版本匹配,直接使用
log::info!(target: "app", "服务已在运行且版本匹配,尝试使用"); log::info!(target: "app", "服务已在运行且版本匹配,尝试使用");
return start_with_existing_service(config_file).await; return start_with_existing_service(config_file).await;
} }
} }
// 强制执行版本检查,如果版本不匹配则重装
if !version_check { if !version_check {
log::info!(target: "app", "服务版本不匹配,尝试重装"); log::info!(target: "app", "服务版本不匹配,尝试重装");
// 获取服务状态,检查是否可以重装
let service_state = ServiceState::get(); let service_state = ServiceState::get();
if !service_state.can_reinstall() { if !service_state.can_reinstall() {
log::warn!(target: "app", "由于限制无法重装服务"); log::warn!(target: "app", "由于限制无法重装服务");
// 尝试直接启动,即使版本不匹配
if let Ok(()) = start_with_existing_service(config_file).await { if let Ok(()) = start_with_existing_service(config_file).await {
log::info!(target: "app", "尽管版本不匹配,但成功启动了服务"); log::info!(target: "app", "尽管版本不匹配,但成功启动了服务");
return Ok(()); return Ok(());
@@ -662,17 +863,14 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
} }
} }
// 尝试重装
log::info!(target: "app", "开始重装服务"); log::info!(target: "app", "开始重装服务");
if let Err(err) = reinstall_service().await { if let Err(err) = reinstall_service().await {
log::warn!(target: "app", "服务重装失败: {}", err); log::warn!(target: "app", "服务重装失败: {}", err);
// 尝试使用现有服务
log::info!(target: "app", "尝试使用现有服务"); log::info!(target: "app", "尝试使用现有服务");
return start_with_existing_service(config_file).await; return start_with_existing_service(config_file).await;
} }
// 重装成功,尝试启动
log::info!(target: "app", "服务重装成功,尝试启动"); log::info!(target: "app", "服务重装成功,尝试启动");
return start_with_existing_service(config_file).await; return start_with_existing_service(config_file).await;
} }
@@ -680,7 +878,6 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
// 检查服务状态 // 检查服务状态
match check_service().await { match check_service().await {
Ok(_) => { Ok(_) => {
// 服务可访问但可能没有运行核心,尝试直接启动
log::info!(target: "app", "服务可用但未运行核心,尝试启动"); log::info!(target: "app", "服务可用但未运行核心,尝试启动");
if let Ok(()) = start_with_existing_service(config_file).await { if let Ok(()) = start_with_existing_service(config_file).await {
return Ok(()); return Ok(());
@@ -695,72 +892,156 @@ pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
if check_service_needs_reinstall().await { if check_service_needs_reinstall().await {
log::info!(target: "app", "服务需要重装"); log::info!(target: "app", "服务需要重装");
// 尝试重装
if let Err(err) = reinstall_service().await { if let Err(err) = reinstall_service().await {
log::warn!(target: "app", "服务重装失败: {}", err); log::warn!(target: "app", "服务重装失败: {}", err);
bail!("Failed to reinstall service: {}", err); bail!("Failed to reinstall service: {}", err);
} }
// 重装后再次尝试启动
log::info!(target: "app", "服务重装完成,尝试启动核心"); log::info!(target: "app", "服务重装完成,尝试启动核心");
start_with_existing_service(config_file).await start_with_existing_service(config_file).await
} else { } else {
// 不需要或不能重装,返回错误
log::warn!(target: "app", "服务不可用且无法重装"); log::warn!(target: "app", "服务不可用且无法重装");
bail!("Service is not available and cannot be reinstalled at this time") bail!("Service is not available and cannot be reinstalled at this time")
} }
} }
/// stop the clash by service /// 通过服务停止core
pub(super) async fn stop_core_by_service() -> Result<()> { pub(super) async fn stop_core_by_service() -> Result<()> {
use crate::utils::network::{NetworkManager, ProxyType}; logging!(debug, Type::Service, "通过服务停止核心 (IPC)");
let url = format!("{SERVICE_URL}/stop_clash"); let payload = serde_json::json!({});
let response = send_ipc_request(IpcCommand::StopClash, payload)
// 使用网络管理器发送POST请求
let client = NetworkManager::global().get_client(ProxyType::NoProxy);
let _ = client
.post(url)
.send()
.await .await
.context("failed to connect to the Clash Verge Service")?; .context("无法连接到Clash Verge Service")?;
if !response.success {
bail!(response.error.unwrap_or_else(|| "停止核心失败".to_string()));
}
if let Some(data) = &response.data {
if let Some(code) = data.get("code") {
let code_value = code.as_u64().unwrap_or(1);
let msg = data
.get("msg")
.and_then(|m| m.as_str())
.unwrap_or("未知错误");
if code_value != 0 {
logging!(
error,
Type::Service,
true,
"停止核心返回错误: code={}, msg={}",
code_value,
msg
);
bail!("停止核心失败: {}", msg);
}
}
}
Ok(()) Ok(())
} }
/// 检查服务是否正在运行 /// 检查服务是否正在运行
pub async fn is_service_running() -> Result<bool> { pub async fn is_service_running() -> Result<bool> {
let resp = check_service().await?; logging!(info, Type::Service, true, "开始检查服务是否正在运行");
// 检查服务状态码和消息 match check_service().await {
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() { Ok(resp) => {
logging!(debug, Type::Service, "Service is running"); if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
Ok(true) logging!(info, Type::Service, true, "服务正在运行");
} else { Ok(true)
logging!(debug, Type::Service, "Service is not running"); } else {
Ok(false) logging!(
warn,
Type::Service,
true,
"服务未正常运行: code={}, msg={}",
resp.code,
resp.msg
);
Ok(false)
}
}
Err(err) => {
logging!(error, Type::Service, true, "检查服务运行状态失败: {}", err);
let error_type = err.root_cause().to_string();
logging!(
error,
Type::Service,
true,
"连接失败的根本原因: {}",
error_type
);
let socket_path = if cfg!(windows) {
r"\\.\pipe\clash-verge-service"
} else {
"/tmp/clash-verge-service.sock"
};
if cfg!(windows) {
logging!(
info,
Type::Service,
true,
"检查Windows命名管道: {}",
socket_path
);
} else {
let socket_exists = std::path::Path::new(socket_path).exists();
logging!(
info,
Type::Service,
true,
"检查Unix套接字文件: {} 是否存在: {}",
socket_path,
socket_exists
);
}
Ok(false)
}
} }
} }
pub async fn is_service_available() -> Result<()> { pub async fn is_service_available() -> Result<()> {
let resp = check_service().await?; logging!(info, Type::Service, true, "开始检查服务是否可用");
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
logging!(debug, Type::Service, "Service is available"); match check_service().await {
Ok(resp) => {
if resp.code == 0 && resp.msg == "ok" && resp.data.is_some() {
logging!(info, Type::Service, true, "服务可用");
} else {
logging!(
warn,
Type::Service,
true,
"服务返回异常: code={}, msg={}",
resp.code,
resp.msg
);
}
Ok(())
}
Err(err) => {
logging!(error, Type::Service, true, "服务不可用: {}", err);
bail!("服务不可用: {}", err)
}
} }
Ok(())
} }
/// 强制重装服务(用于UI中的修复服务按钮) /// 强制重装服务(UI修复按钮)
pub async fn force_reinstall_service() -> Result<()> { pub async fn force_reinstall_service() -> Result<()> {
log::info!(target: "app", "用户请求强制重装服务"); log::info!(target: "app", "用户请求强制重装服务");
// 创建默认服务状态(重置所有限制)
let service_state = ServiceState::default(); let service_state = ServiceState::default();
service_state.save()?; service_state.save()?;
log::info!(target: "app", "已重置服务状态,开始执行重装"); log::info!(target: "app", "已重置服务状态,开始执行重装");
// 执行重装
match reinstall_service().await { match reinstall_service().await {
Ok(()) => { Ok(()) => {
log::info!(target: "app", "服务重装成功"); log::info!(target: "app", "服务重装成功");
@@ -772,3 +1053,178 @@ pub async fn force_reinstall_service() -> Result<()> {
} }
} }
} }
/*
/// 彻底诊断服务状态检查安装状态、IPC通信和服务版本
pub async fn diagnose_service() -> Result<()> {
logging!(info, Type::Service, true, "============= 开始服务诊断 =============");
// 1. 检查服务文件是否存在
let service_path = dirs::service_path();
match service_path {
Ok(path) => {
let service_exists = path.exists();
logging!(info, Type::Service, true, "服务可执行文件路径: {:?}, 存在: {}", path, service_exists);
if !service_exists {
logging!(error, Type::Service, true, "服务可执行文件不存在,需要重新安装");
bail!("服务可执行文件不存在,需要重新安装");
}
// 检查服务版本文件
let version_file = path.with_file_name("version.txt");
if version_file.exists() {
match std::fs::read_to_string(&version_file) {
Ok(content) => {
logging!(info, Type::Service, true, "服务版本文件内容: {}", content.trim());
}
Err(e) => {
logging!(warn, Type::Service, true, "读取服务版本文件失败: {}", e);
}
}
} else {
logging!(warn, Type::Service, true, "服务版本文件不存在: {:?}", version_file);
}
}
Err(e) => {
logging!(error, Type::Service, true, "获取服务路径失败: {}", e);
bail!("获取服务路径失败: {}", e);
}
}
// 2. 检查IPC通信 - 命名管道/Unix套接字
let socket_path = if cfg!(windows) {
r"\\.\pipe\clash-verge-service"
} else {
"/tmp/clash-verge-service.sock"
};
logging!(info, Type::Service, true, "IPC通信路径: {}", socket_path);
if !cfg!(windows) {
// Unix系统检查套接字文件是否存在
let socket_exists = std::path::Path::new(socket_path).exists();
logging!(info, Type::Service, true, "Unix套接字文件是否存在: {}", socket_exists);
if !socket_exists {
logging!(warn, Type::Service, true, "Unix套接字文件不存在服务可能未运行");
}
}
// 3. 尝试通过IPC检查服务状态
logging!(info, Type::Service, true, "尝试通过IPC通信检查服务状态...");
match check_service().await {
Ok(resp) => {
logging!(info, Type::Service, true, "服务状态检查成功: code={}, msg={}", resp.code, resp.msg);
// 4. 检查服务版本
match check_service_version().await {
Ok(version) => {
logging!(info, Type::Service, true, "服务版本: {}, 要求版本: {}",
version, REQUIRED_SERVICE_VERSION);
if version != REQUIRED_SERVICE_VERSION {
logging!(warn, Type::Service, true, "服务版本不匹配,建议重装服务");
} else {
logging!(info, Type::Service, true, "服务版本匹配");
}
}
Err(err) => {
logging!(error, Type::Service, true, "检查服务版本失败: {}", err);
}
}
}
Err(err) => {
logging!(error, Type::Service, true, "服务状态检查失败: {}", err);
// 5. 检查系统服务状态 - Windows专用
#[cfg(windows)]
{
use std::process::Command;
logging!(info, Type::Service, true, "尝试检查Windows服务状态...");
let output = Command::new("sc")
.args(["query", "clash_verge_service"])
.output();
match output {
Ok(out) => {
let stdout = String::from_utf8_lossy(&out.stdout);
let contains_running = stdout.contains("RUNNING");
logging!(info, Type::Service, true, "Windows服务查询结果: {}",
if contains_running { "正在运行" } else { "未运行" });
if !contains_running {
logging!(info, Type::Service, true, "服务输出: {}", stdout);
}
}
Err(e) => {
logging!(error, Type::Service, true, "检查Windows服务状态失败: {}", e);
}
}
}
// macOS专用
#[cfg(target_os = "macos")]
{
use std::process::Command;
logging!(info, Type::Service, true, "尝试检查macOS服务状态...");
let output = Command::new("launchctl")
.args(["list", "io.github.clash-verge-rev.clash-verge-rev.service"])
.output();
match output {
Ok(out) => {
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
if out.status.success() {
logging!(info, Type::Service, true, "macOS服务正在运行");
logging!(debug, Type::Service, true, "服务详情: {}", stdout);
} else {
logging!(warn, Type::Service, true, "macOS服务未运行");
if !stderr.is_empty() {
logging!(info, Type::Service, true, "错误信息: {}", stderr);
}
}
}
Err(e) => {
logging!(error, Type::Service, true, "检查macOS服务状态失败: {}", e);
}
}
}
// Linux专用
#[cfg(target_os = "linux")]
{
use std::process::Command;
logging!(info, Type::Service, true, "尝试检查Linux服务状态...");
let output = Command::new("systemctl")
.args(["status", "clash_verge_service"])
.output();
match output {
Ok(out) => {
let stdout = String::from_utf8_lossy(&out.stdout);
let is_active = stdout.contains("Active: active (running)");
logging!(info, Type::Service, true, "Linux服务状态: {}",
if is_active { "活跃运行中" } else { "未运行" });
if !is_active {
logging!(info, Type::Service, true, "服务状态详情: {}", stdout);
}
}
Err(e) => {
logging!(error, Type::Service, true, "检查Linux服务状态失败: {}", e);
}
}
}
}
}
logging!(info, Type::Service, true, "============= 服务诊断完成 =============");
Ok(())
} */

View File

@@ -0,0 +1,484 @@
use crate::logging;
use crate::utils::logging::Type;
use anyhow::{bail, Context, Result};
use hmac::{Hmac, Mac};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::time::{SystemTime, UNIX_EPOCH};
const IPC_SOCKET_NAME: &str = if cfg!(windows) {
r"\\.\pipe\clash-verge-service"
} else {
"/tmp/clash-verge-service.sock"
};
// 定义命令类型
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum IpcCommand {
GetClash,
GetVersion,
StartClash,
StopClash,
}
// IPC消息格式
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IpcRequest {
pub id: String,
pub timestamp: u64,
pub command: IpcCommand,
pub payload: serde_json::Value,
pub signature: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IpcResponse {
pub id: String,
pub success: bool,
pub data: Option<serde_json::Value>,
pub error: Option<String>,
pub signature: String,
}
// 密钥派生函数
fn derive_secret_key() -> Vec<u8> {
// to do
// 从系统安全存储中获取或从程序安装时生成的密钥文件中读取
let unique_app_id = "clash-verge-app-secret-fuck-me-until-daylight";
let mut hasher = Sha256::new();
hasher.update(unique_app_id.as_bytes());
hasher.finalize().to_vec()
}
// 创建带签名的请求
pub fn create_signed_request(
command: IpcCommand,
payload: serde_json::Value,
) -> Result<IpcRequest> {
let id = nanoid::nanoid!(32);
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let unsigned_request = IpcRequest {
id: id.clone(),
timestamp,
command: command.clone(),
payload: payload.clone(),
signature: String::new(),
};
let unsigned_json = serde_json::to_string(&unsigned_request)?;
let signature = sign_message(&unsigned_json)?;
Ok(IpcRequest {
id,
timestamp,
command,
payload,
signature,
})
}
// 签名消息
fn sign_message(message: &str) -> Result<String> {
type HmacSha256 = Hmac<Sha256>;
let secret_key = derive_secret_key();
let mut mac = HmacSha256::new_from_slice(&secret_key).context("HMAC初始化失败")?;
mac.update(message.as_bytes());
let result = mac.finalize();
let signature = hex::encode(result.into_bytes());
Ok(signature)
}
// 验证响应签名
pub fn verify_response_signature(response: &IpcResponse) -> Result<bool> {
let verification_response = IpcResponse {
id: response.id.clone(),
success: response.success,
data: response.data.clone(),
error: response.error.clone(),
signature: String::new(),
};
let message = serde_json::to_string(&verification_response)?;
let expected_signature = sign_message(&message)?;
Ok(expected_signature == response.signature)
}
// IPC连接管理-win
#[cfg(target_os = "windows")]
pub async fn send_ipc_request(
command: IpcCommand,
payload: serde_json::Value,
) -> Result<IpcResponse> {
use std::ffi::CString;
use std::fs::File;
use std::io::{Read, Write};
use std::os::windows::io::{FromRawHandle, RawHandle};
use std::ptr;
use winapi::um::fileapi::CreateFileA;
use winapi::um::winbase::{PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_WAIT};
use winapi::um::winnt::OPEN_EXISTING;
use winapi::um::winnt::{
FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE,
};
logging!(
info,
Type::Service,
true,
"准备发送IPC请求到Windows命名管道: {}",
IPC_SOCKET_NAME
);
logging!(
debug,
Type::Service,
true,
"IPC请求: 命令={:?}, 数据大小={}字节",
command,
serde_json::to_string(&payload)?.len()
);
let command_type = format!("{:?}", command);
let request = match create_signed_request(command, payload) {
Ok(req) => {
logging!(
debug,
Type::Service,
true,
"创建签名请求成功: ID={}",
req.id
);
req
}
Err(e) => {
logging!(error, Type::Service, true, "创建签名请求失败: {}", e);
return Err(e);
}
};
let request_json = serde_json::to_string(&request)?;
logging!(
debug,
Type::Service,
true,
"请求JSON大小: {}字节",
request_json.len()
);
logging!(debug, Type::Service, true, "尝试连接Windows命名管道...");
let result = tokio::task::spawn_blocking(move || -> Result<IpcResponse> {
let c_pipe_name = match CString::new(IPC_SOCKET_NAME) {
Ok(name) => name,
Err(e) => {
logging!(error, Type::Service, true, "创建CString失败: {}", e);
return Err(anyhow::anyhow!("创建CString失败: {}", e));
}
};
let handle = unsafe {
CreateFileA(
c_pipe_name.as_ptr(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
ptr::null_mut(),
OPEN_EXISTING,
0,
ptr::null_mut(),
)
};
if handle == winapi::um::handleapi::INVALID_HANDLE_VALUE {
let error = std::io::Error::last_os_error();
logging!(error, Type::Service, true, "连接到命名管道失败: {}", error);
return Err(anyhow::anyhow!("无法连接到服务命名管道: {}", error));
}
let mut pipe = unsafe { File::from_raw_handle(handle as RawHandle) };
logging!(info, Type::Service, true, "成功连接到Windows命名管道");
let request_bytes = request_json.as_bytes();
let len_bytes = (request_bytes.len() as u32).to_be_bytes();
logging!(debug, Type::Service, true, "发送IPC请求...");
if let Err(e) = pipe.write_all(&len_bytes) {
logging!(error, Type::Service, true, "写入请求长度失败: {}", e);
return Err(anyhow::anyhow!("写入请求长度失败: {}", e));
}
if let Err(e) = pipe.write_all(request_bytes) {
logging!(error, Type::Service, true, "写入请求内容失败: {}", e);
return Err(anyhow::anyhow!("写入请求内容失败: {}", e));
}
logging!(debug, Type::Service, true, "请求已发送,等待响应...");
if let Err(e) = pipe.flush() {
logging!(error, Type::Service, true, "刷新管道失败: {}", e);
return Err(anyhow::anyhow!("刷新管道失败: {}", e));
}
let mut response_len_bytes = [0u8; 4];
if let Err(e) = pipe.read_exact(&mut response_len_bytes) {
logging!(error, Type::Service, true, "读取响应长度失败: {}", e);
return Err(anyhow::anyhow!("读取响应长度失败: {}", e));
}
let response_len = u32::from_be_bytes(response_len_bytes) as usize;
logging!(
debug,
Type::Service,
true,
"收到响应长度: {}字节",
response_len
);
let mut response_bytes = vec![0u8; response_len];
if let Err(e) = pipe.read_exact(&mut response_bytes) {
logging!(error, Type::Service, true, "读取响应内容失败: {}", e);
return Err(anyhow::anyhow!("读取响应内容失败: {}", e));
}
logging!(
debug,
Type::Service,
true,
"成功接收响应: {}字节",
response_bytes.len()
);
let response: IpcResponse = match serde_json::from_slice::<IpcResponse>(&response_bytes) {
Ok(r) => {
logging!(
debug,
Type::Service,
true,
"响应解析成功: success={}",
r.success
);
r
}
Err(e) => {
logging!(
error,
Type::Service,
true,
"解析响应失败: {}, 原始数据: {:?}",
e,
String::from_utf8_lossy(
&response_bytes[..std::cmp::min(100, response_bytes.len())]
)
);
return Err(anyhow::anyhow!("解析响应失败: {}", e));
}
};
match verify_response_signature(&response) {
Ok(valid) => {
if !valid {
logging!(error, Type::Service, true, "服务响应签名验证失败");
bail!("服务响应签名验证失败");
}
logging!(debug, Type::Service, true, "响应签名验证成功");
}
Err(e) => {
logging!(error, Type::Service, true, "验证响应签名时出错: {}", e);
return Err(e);
}
}
logging!(
info,
Type::Service,
true,
"IPC请求完成: 命令={}, 成功={}",
command_type,
response.success
);
Ok(response)
})
.await?;
Ok(result)
}
// IPC连接管理-unix
#[cfg(target_family = "unix")]
pub async fn send_ipc_request(
command: IpcCommand,
payload: serde_json::Value,
) -> Result<IpcResponse> {
use std::os::unix::net::UnixStream;
logging!(
info,
Type::Service,
true,
"准备发送IPC请求到Unix套接字: {}",
IPC_SOCKET_NAME
);
logging!(
debug,
Type::Service,
true,
"IPC请求: 命令={:?}, 数据大小={}字节",
command,
serde_json::to_string(&payload)?.len()
);
let command_type = format!("{:?}", command);
let request = match create_signed_request(command, payload) {
Ok(req) => {
logging!(
debug,
Type::Service,
true,
"创建签名请求成功: ID={}",
req.id
);
req
}
Err(e) => {
logging!(error, Type::Service, true, "创建签名请求失败: {}", e);
return Err(e);
}
};
let request_json = serde_json::to_string(&request)?;
logging!(
debug,
Type::Service,
true,
"请求JSON大小: {}字节",
request_json.len()
);
logging!(debug, Type::Service, true, "尝试连接Unix套接字...");
let socket_exists = std::path::Path::new(IPC_SOCKET_NAME).exists();
logging!(
debug,
Type::Service,
true,
"Unix套接字文件 {} 是否存在: {}",
IPC_SOCKET_NAME,
socket_exists
);
let mut stream = match UnixStream::connect(IPC_SOCKET_NAME) {
Ok(s) => {
logging!(info, Type::Service, true, "成功连接到Unix套接字");
s
}
Err(e) => {
logging!(error, Type::Service, true, "连接到Unix套接字失败: {}", e);
return Err(anyhow::anyhow!("无法连接到服务Unix套接字: {}", e));
}
};
let request_bytes = request_json.as_bytes();
let len_bytes = (request_bytes.len() as u32).to_be_bytes();
logging!(debug, Type::Service, true, "发送IPC请求...");
if let Err(e) = std::io::Write::write_all(&mut stream, &len_bytes) {
logging!(error, Type::Service, true, "写入请求长度失败: {}", e);
return Err(anyhow::anyhow!("写入请求长度失败: {}", e));
}
if let Err(e) = std::io::Write::write_all(&mut stream, request_bytes) {
logging!(error, Type::Service, true, "写入请求内容失败: {}", e);
return Err(anyhow::anyhow!("写入请求内容失败: {}", e));
}
logging!(debug, Type::Service, true, "请求已发送,等待响应...");
let mut response_len_bytes = [0u8; 4];
if let Err(e) = std::io::Read::read_exact(&mut stream, &mut response_len_bytes) {
logging!(error, Type::Service, true, "读取响应长度失败: {}", e);
return Err(anyhow::anyhow!("读取响应长度失败: {}", e));
}
let response_len = u32::from_be_bytes(response_len_bytes) as usize;
logging!(
debug,
Type::Service,
true,
"收到响应长度: {}字节",
response_len
);
let mut response_bytes = vec![0u8; response_len];
if let Err(e) = std::io::Read::read_exact(&mut stream, &mut response_bytes) {
logging!(error, Type::Service, true, "读取响应内容失败: {}", e);
return Err(anyhow::anyhow!("读取响应内容失败: {}", e));
}
logging!(
debug,
Type::Service,
true,
"成功接收响应: {}字节",
response_bytes.len()
);
let response: IpcResponse = match serde_json::from_slice::<IpcResponse>(&response_bytes) {
Ok(r) => {
logging!(
debug,
Type::Service,
true,
"响应解析成功: success={}",
r.success
);
r
}
Err(e) => {
logging!(
error,
Type::Service,
true,
"解析响应失败: {}, 原始数据: {:?}",
e,
String::from_utf8_lossy(
&response_bytes[..std::cmp::min(100, response_bytes.len())]
)
);
return Err(anyhow::anyhow!("解析响应失败: {}", e));
}
};
match verify_response_signature(&response) {
Ok(valid) => {
if !valid {
logging!(error, Type::Service, true, "服务响应签名验证失败");
bail!("服务响应签名验证失败");
}
logging!(debug, Type::Service, true, "响应签名验证成功");
}
Err(e) => {
logging!(error, Type::Service, true, "验证响应签名时出错: {}", e);
return Err(e);
}
}
logging!(
info,
Type::Service,
true,
"IPC请求完成: 命令={}, 成功={}",
command_type,
response.success
);
Ok(response)
}

View File

@@ -105,6 +105,20 @@ pub async fn resolve_setup(app: &mut App) {
logging_error!(Type::Setup, true, init::init_resources()); logging_error!(Type::Setup, true, init::init_resources());
logging_error!(Type::Setup, true, init::init_scheme()); logging_error!(Type::Setup, true, init::init_scheme());
logging_error!(Type::Setup, true, init::startup_script().await); logging_error!(Type::Setup, true, init::startup_script().await);
// 诊断服务状态
/* logging!(info, Type::Service, true, "执行服务状态诊断");
{
match crate::core::service::diagnose_service().await {
Ok(_) => {
logging!(info, Type::Service, true, "服务诊断完成");
},
Err(e) => {
logging!(error, Type::Service, true, "服务诊断出错: {}", e);
},
}
} */
// 处理随机端口 // 处理随机端口
logging_error!(Type::System, true, resolve_random_port_config()); logging_error!(Type::System, true, resolve_random_port_config());
// 启动核心 // 启动核心