From 7d1310a0024007b01afb93f7d33b82b34c6bd3c2 Mon Sep 17 00:00:00 2001 From: jiangcuo Date: Wed, 16 Apr 2025 00:24:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + src/main.rs | 510 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 309 insertions(+), 202 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fc51344..4c0a10e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ windows = { version = "0.48", features = ["Win32_Foundation", "Win32_Security", wmi = "0.15.2" regex = "1.11.1" chrono = "0.4.40" +netuser-rs = { git = "https://github.com/secur30nly/netuser-rs.git"} diff --git a/src/main.rs b/src/main.rs index ffab68d..73df019 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ use std::os::windows::process::CommandExt; use wmi::{COMLibrary, WMIConnection, Variant, FilterValue}; use std::collections::HashMap; use regex; +use netuser_rs; fn is_admin() -> bool { unsafe { @@ -143,7 +144,9 @@ struct VirtualMachine { struct IpInfo { mac_address: String, switch_name: String, - ip_addresses: Vec, + ipv4_addresses: Vec, + ipv6_addresses: Vec, + fqdn: String, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -151,7 +154,9 @@ struct NetworkAdapter { macaddress: String, vmid: String, switchname: String, - ipaddresses: Vec, + ipv4_addresses: Vec, + ipv6_addresses: Vec, + fqdn: String, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -183,49 +188,40 @@ fn variant_to_string(v: &Variant) -> Option { } async fn stop_vm(vmid: &str) -> Result { - let wmi_con = get_wmi_connection()?; - - // 查询虚拟机 - let mut filters = HashMap::new(); - filters.insert("Name".to_string(), FilterValue::String(vmid.to_string())); - let vms: Vec> = wmi_con.filtered_query(&filters)?; - - if vms.is_empty() { - return Ok("VM not found".to_string()); - } - - // 使用PowerShell停止虚拟机,因为WMI库中exec_method实现有问题 + // 使用PowerShell的Stop-VM命令异步停止虚拟机 + // -Force参数确保强制关闭,类似于断电 + // 使用Start-Job在后台运行,不等待完成 let output = Command::new("powershell.exe") - .args(["-Command", &format!("$OutputEncoding = [System.Text.Encoding]::UTF8; Get-CimInstance -Namespace root\\virtualization\\v2 -ClassName Msvm_ComputerSystem -Filter \"Name = '{}'\" | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{{RequestedState=3; Force=$true}}", vmid)]) + .args(["-Command", &format!("Get-VM -Id '{}' | Stop-VM -Force -Confirm:$false | Out-Null", vmid)]) .output()?; - Ok(String::from_utf8_lossy(&output.stdout).to_string()) + if output.status.success() { + Ok("已关机".to_string()) + } else { + let error = String::from_utf8_lossy(&output.stderr); + Ok(format!("关机失败: {}", error)) + } } async fn start_vm(vmid: &str) -> Result { - let wmi_con = get_wmi_connection()?; - - // 查询虚拟机 - let mut filters = HashMap::new(); - filters.insert("Name".to_string(), FilterValue::String(vmid.to_string())); - let vms: Vec> = wmi_con.filtered_query(&filters)?; - - if vms.is_empty() { - return Ok("VM not found".to_string()); - } - - // 使用PowerShell启动虚拟机,因为WMI库中exec_method实现有问题 + // 使用PowerShell的Start-VM命令异步启动虚拟机 + // 使用Start-Job在后台运行,不等待完成 let output = Command::new("powershell.exe") - .args(["-Command", &format!("$OutputEncoding = [System.Text.Encoding]::UTF8; Get-CimInstance -Namespace root\\virtualization\\v2 -ClassName Msvm_ComputerSystem -Filter \"Name = '{}'\" | Invoke-CimMethod -MethodName RequestStateChange -Arguments @{{RequestedState=2}}", vmid)]) + .args(["-Command", &format!("Get-VM -Id '{}' | Start-VM | Out-Null", vmid)]) .output()?; - Ok(String::from_utf8_lossy(&output.stdout).to_string()) + if output.status.success() { + Ok("已启动".to_string()) + } else { + let error = String::from_utf8_lossy(&output.stderr); + Ok(format!("启动失败: {}", error)) + } } async fn list_vm_details(vmid: &str) -> Result { let wmi_con = get_wmi_connection()?; - // 查询特定虚拟机 + // 查询虚拟机是否存在 let mut filters = HashMap::new(); filters.insert("Name".to_string(), FilterValue::String(vmid.to_string())); let vms: Vec> = wmi_con.filtered_query(&filters)?; @@ -277,7 +273,7 @@ async fn list_vm() -> Result { // 获取内存大小 let memory_mb = get_vm_memory(&wmi_con, &vm_id)?; - + // 创建VM对象 vms.push(VirtualMachine { vmid: vm_id, @@ -294,93 +290,287 @@ async fn list_vm() -> Result { Ok(json) } -// 获取虚拟机网络信息 -fn get_vm_network_info(wmi_con: &WMIConnection, vm_id: &str) -> Result { - println!("获取VM {} 的网络信息", vm_id); +fn get_vm_fqdn(wmi_con: &WMIConnection, vm_id: &str) -> Result { + // 查询虚拟机的运行状态 + let vm_query = format!("SELECT * FROM Msvm_ComputerSystem WHERE Name='{}'", vm_id); + let vms: Vec> = wmi_con.raw_query(&vm_query)?; - // 查询网络适配器 - let net_query = format!("SELECT * FROM Msvm_SyntheticEthernetPortSettingData WHERE InstanceID LIKE '%{}%'", vm_id); - let network_adapters: Vec> = wmi_con.raw_query(&net_query)?; - - if network_adapters.is_empty() { - println!("没有找到网络适配器"); - return Ok(IpInfo { - mac_address: String::new(), - switch_name: String::new(), - ip_addresses: Vec::new(), - }); + if vms.is_empty() { + return Ok(String::new()); } - // 获取第一个网络适配器信息 - let adapter = &network_adapters[0]; - let mac_address = adapter.get("Address").and_then(variant_to_string).unwrap_or_default(); - let switch_name = adapter.get("SwitchName").and_then(variant_to_string).unwrap_or_default(); + // 检查虚拟机是否正在运行 + let running = match vms[0].get("EnabledState") { + Some(Variant::I2(2)) | Some(Variant::I4(2)) | Some(Variant::UI2(2)) => true, + _ => false + }; - // 尝试获取IP地址 - let mut ip_addresses = Vec::new(); + if !running { + return Ok(String::new()); + } - // 查询关联的IP配置 - if !mac_address.is_empty() { - // 通过虚拟机的管理服务获取IP地址 - if is_uuid(vm_id) { - // 首先获取GuestIntrinsicExchangeItems,可能包含IP地址 - let guestinfo_query = format!( - "ASSOCIATORS OF {{Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='{}'}} \ - WHERE AssocClass=Msvm_SystemDevice ResultClass=Msvm_KvpExchangeComponent", - vm_id - ); + // 获取Msvm_KvpExchangeComponent关联组件 + let kvp_query = format!( + "ASSOCIATORS OF {{Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='{}'}} \ + WHERE AssocClass=Msvm_SystemDevice ResultClass=Msvm_KvpExchangeComponent", + vm_id + ); + + let kvp_components: Vec> = wmi_con.raw_query(&kvp_query)?; + + if kvp_components.is_empty() { + return Ok(String::new()); + } + + // 获取GuestIntrinsicExchangeItems属性 + if let Some(Variant::Array(exchange_items)) = kvp_components[0].get("GuestIntrinsicExchangeItems") { + // 遍历XML字符串数组 + for item in exchange_items { + if let Variant::String(xml_str) = item { + // 解析XML字符串,查找FullyQualifiedDomainName + if xml_str.contains("FullyQualifiedDomainName") && xml_str.contains(""); + let value_end = xml_str[start_pos..].find(""); + + if let (Some(vs), Some(ve)) = (value_start, value_end) { + let value_content = &xml_str[start_pos + vs + 7..start_pos + ve]; + if !value_content.trim().is_empty() { + return Ok(value_content.trim().to_string()); + } + } + } + } + } + } + } + + Ok(String::new()) +} + +// 获取虚拟机网络信息 +async fn get_vm_network_info(vm_id: Option<&str>) -> Result { + let wmi_con = get_wmi_connection()?; + + match vm_id { + Some(id) => { - let guest_info: Vec> = wmi_con.raw_query(&guestinfo_query)?; + // 查询网络适配器 + let net_query = format!("SELECT * FROM Msvm_SyntheticEthernetPortSettingData WHERE InstanceID LIKE '%{}%'", id); + let network_adapters: Vec> = wmi_con.raw_query(&net_query)?; - if !guest_info.is_empty() { - // 解析GuestExchangeItems以获取IP地址 - if let Some(exchange_items) = guest_info[0].get("GuestExchangeItems").and_then(variant_to_string) { - for line in exchange_items.lines() { - if line.contains("NetworkAddressIPv4") || line.contains("NetworkAddressIPv6") { - if let Some(ip) = line.split_whitespace().last() { - if !ip.is_empty() && !ip_addresses.contains(&ip.to_string()) { - ip_addresses.push(ip.to_string()); + if network_adapters.is_empty() { + return Ok(serde_json::to_string(&serde_json::json!({ + "MacAddress": "", + "SwitchName": "", + "Ipv4Addresses": [], + "Ipv6Addresses": [], + "FQDN": "", + "Running": false + }))?); + } + + // 获取第一个网络适配器信息 + let adapter = &network_adapters[0]; + let mac_address = match adapter.get("Address") { + Some(Variant::String(s)) => s.clone(), + _ => String::new() + }; + + + // 查询虚拟机状态 + let vm_query = format!("SELECT * FROM Msvm_ComputerSystem WHERE Name='{}'", id); + let vms: Vec> = wmi_con.raw_query(&vm_query)?; + + // 默认假设虚拟机未运行 + let mut running = false; + + if !vms.is_empty() { + let state = match vms[0].get("EnabledState") { + Some(Variant::I2(2)) => 2, + Some(Variant::I4(2)) => 2, + Some(Variant::UI2(2)) => 2, + _ => 0 + }; + running = state == 2; + } + + + // 尝试获取IP地址 + let mut ipv4_addresses = Vec::new(); + let mut ipv6_addresses = Vec::new(); + + // 获取FQDN + let fqdn = get_vm_fqdn(&wmi_con, id)?; + + // 只有虚拟机运行时才尝试获取IP地址 + if running && !mac_address.is_empty() { + // 方法2: 查询所有Msvm_GuestNetworkAdapterConfiguration,尝试匹配VM ID或MAC地址 + let mac_no_colon = mac_address.replace(":", ""); + let all_ip_query = "SELECT * FROM Msvm_GuestNetworkAdapterConfiguration"; + + let all_ip_configs: Vec> = match wmi_con.raw_query(all_ip_query) { + Ok(configs) => { + configs + }, + Err(e) => { + Vec::new() + } + }; + + for config in &all_ip_configs { + if let Some(Variant::String(instance_id)) = config.get("InstanceID") { + // 如果ID包含VM ID或MAC地址,则提取IP + if instance_id.contains(id) || instance_id.contains(&mac_no_colon) { + + // 获取IP地址并分类 + if let Some(Variant::Array(addresses)) = config.get("IPAddresses") { + for address in addresses { + if let Variant::String(ip) = address { + if !ip.is_empty() { + // 判断是IPv4还是IPv6 + if ip.contains(':') { + // IPv6 + let already_exists = ipv6_addresses.iter().any(|existing: &String| existing == ip); + if !already_exists { + ipv6_addresses.push(ip.clone()); + } + } else { + // IPv4 + let already_exists = ipv4_addresses.iter().any(|existing: &String| existing == ip); + if !already_exists { + ipv4_addresses.push(ip.clone()); + } + } + } + } } } } } } } - } - - // 如果上面方法没有找到IP,尝试其他方式 - if ip_addresses.is_empty() { - // 尝试查询连接到具体网络适配器的IP地址信息 - let ip_query = format!( - "SELECT * FROM Msvm_GuestNetworkAdapterConfiguration \ - WHERE InstanceID LIKE '%{}%'", - mac_address.replace(":", "") - ); - let ip_configs: Vec> = wmi_con.raw_query(&ip_query)?; + // 构建结果JSON + let result = serde_json::json!({ + "MacAddress": mac_address, + "ipv4": ipv4_addresses, + "ipv6": ipv6_addresses, + "FQDN": fqdn, + }); - if !ip_configs.is_empty() { - // 尝试从IPAddresses获取IP - if let Some(Variant::Array(ip_array)) = ip_configs[0].get("IPAddresses") { - for ip_var in ip_array { - if let Some(ip) = variant_to_string(ip_var) { - if !ip.is_empty() && !ip_addresses.contains(&ip) { - ip_addresses.push(ip); + Ok(serde_json::to_string(&result)?) + }, + None => { + println!("获取所有VM的网络信息"); + + // 获取所有虚拟机 + let vm_query = "SELECT * FROM Msvm_ComputerSystem WHERE Caption='虚拟机' OR Caption='Virtual Machine'"; + let vms: Vec> = wmi_con.raw_query(vm_query)?; + + let mut results = Vec::new(); + + for vm in &vms { + let vm_id = match vm.get("Name") { + Some(Variant::String(s)) => s.clone(), + _ => continue + }; + + let vm_name = match vm.get("ElementName") { + Some(Variant::String(s)) => s.clone(), + _ => String::new() + }; + + // 获取虚拟机状态 + let running = match vm.get("EnabledState") { + Some(Variant::I2(2)) | Some(Variant::I4(2)) | Some(Variant::UI2(2)) => true, + _ => false + }; + + // 查询网络适配器 + let net_query = format!("SELECT * FROM Msvm_SyntheticEthernetPortSettingData WHERE InstanceID LIKE '%{}%'", vm_id); + let network_adapters: Vec> = wmi_con.raw_query(&net_query)?; + + for adapter in &network_adapters { + let mac_address = match adapter.get("Address") { + Some(Variant::String(s)) => s.clone(), + _ => String::new() + }; + + let switch_name = match adapter.get("SwitchName") { + Some(Variant::String(s)) => s.clone(), + _ => String::new() + }; + + // 尝试获取IP地址 + let mut ipv4_addresses = Vec::new(); + let mut ipv6_addresses = Vec::new(); + + // 获取FQDN + let fqdn = get_vm_fqdn(&wmi_con, &vm_id)?; + + if running && !mac_address.is_empty() { + // 方法2: 尝试查询所有配置 + let mac_no_colon = mac_address.replace(":", ""); + let all_ip_query = "SELECT * FROM Msvm_GuestNetworkAdapterConfiguration"; + + let all_ip_configs: Vec> = match wmi_con.raw_query(all_ip_query) { + Ok(configs) => configs, + Err(_) => Vec::new() + }; + + for config in &all_ip_configs { + if let Some(Variant::String(instance_id)) = config.get("InstanceID") { + if instance_id.contains(&vm_id) || instance_id.contains(&mac_no_colon) { + // 获取IP地址并分类 + if let Some(Variant::Array(addresses)) = config.get("IPAddresses") { + for address in addresses { + if let Variant::String(ip) = address { + if !ip.is_empty() { + // 判断是IPv4还是IPv6 + if ip.contains(':') { + // IPv6 + let already_exists = ipv6_addresses.iter().any(|existing: &String| existing == ip); + if !already_exists { + ipv6_addresses.push(ip.clone()); + } + } else { + // IPv4 + let already_exists = ipv4_addresses.iter().any(|existing: &String| existing == ip); + if !already_exists { + ipv4_addresses.push(ip.clone()); + } + } + } + } + } + } + } } } } + + // 构建结果 + let result = serde_json::json!({ + "VmId": vm_id, + "VmName": vm_name, + "MacAddress": mac_address, + "SwitchName": switch_name, + "Ipv4Addresses": ipv4_addresses, + "Ipv6Addresses": ipv6_addresses, + "FQDN": fqdn, + "Running": running + }); + + results.push(result); } } + + Ok(serde_json::to_string(&results)?) } } - - println!("网络信息: MAC={}, Switch={}, IPs={:?}", mac_address, switch_name, ip_addresses); - - Ok(IpInfo { - mac_address, - switch_name, - ip_addresses, - }) } // 获取虚拟机CPU核心数 @@ -416,29 +606,6 @@ fn get_vm_cpu_cores(wmi_con: &WMIConnection, vm_id: &str) -> Result { // 获取虚拟机内存大小(MB) fn get_vm_memory(wmi_con: &WMIConnection, vm_id: &str) -> Result { - // 方法1: 通过内存设置关联查询 - let query1 = format!( - "ASSOCIATORS OF {{Msvm_ComputerSystem.CreationClassName='Msvm_ComputerSystem',Name='{}'}} \ - WHERE AssocClass=Msvm_SettingsDefineState ResultClass=Msvm_MemorySettingData", - vm_id - ); - - let memory_settings1: Vec> = wmi_con.raw_query(&query1)?; - - if !memory_settings1.is_empty() { - // 从VirtualQuantity获取内存大小(MB) - if let Some(mem) = memory_settings1[0].get("VirtualQuantity") { - match mem { - Variant::I4(mem) => return Ok(*mem as u64), - Variant::UI4(mem) => return Ok(*mem as u64), - Variant::I8(mem) => return Ok(*mem as u64), - Variant::UI8(mem) => return Ok(*mem), - _ => println!("方法1: 内存大小类型不匹配: {:?}", mem), - } - } - } - - // 方法2: 尝试直接查询内存设置 let query2 = format!( "SELECT * FROM Msvm_MemorySettingData WHERE InstanceID LIKE '%{}%'", vm_id @@ -458,26 +625,6 @@ fn get_vm_memory(wmi_con: &WMIConnection, vm_id: &str) -> Result { } } - // 方法3: 查询虚拟机配置 - let query3 = format!( - "SELECT * FROM Msvm_VirtualSystemSettingData WHERE InstanceID LIKE '%{}%'", - vm_id - ); - - let vm_settings: Vec> = wmi_con.raw_query(&query3)?; - - if !vm_settings.is_empty() { - if let Some(mem) = vm_settings[0].get("MemoryStartup") { - match mem { - Variant::I4(mem) => return Ok(*mem as u64), - Variant::UI4(mem) => return Ok(*mem as u64), - Variant::I8(mem) => return Ok(*mem as u64), - Variant::UI8(mem) => return Ok(*mem), - _ => println!("方法3: 内存大小类型不匹配: {:?}", mem), - } - } - } - // 默认返回1GB println!("无法确定内存大小,使用默认值: 1024MB"); Ok(1024) @@ -527,55 +674,6 @@ async fn get_all() -> Result { Ok(json) } -async fn get_network(vmid: Option<&str>) -> Result { - let wmi_con = get_wmi_connection()?; - - let mut adapters = Vec::new(); - - // 根据是否有VMID选择查询方式 - if let Some(id) = vmid { - // 先获取特定虚拟机 - let mut vm_filters = HashMap::new(); - vm_filters.insert("Name".to_string(), FilterValue::String(id.to_string())); - let vms: Vec> = wmi_con.filtered_query(&vm_filters)?; - - if !vms.is_empty() { - // 查询该虚拟机的网络适配器 - let wql = format!("SELECT * FROM Msvm_SyntheticEthernetPortSettingData WHERE InstanceID LIKE '%{}%'", id); - let network_adapters: Vec> = wmi_con.raw_query(&wql)?; - - for adapter in network_adapters { - adapters.push(NetworkAdapter { - macaddress: adapter.get("Address").and_then(variant_to_string).unwrap_or_default(), - vmid: id.to_string(), - switchname: adapter.get("SwitchName").and_then(variant_to_string).unwrap_or_default(), - ipaddresses: Vec::new(), // WMI中不直接提供IP地址,需要额外查询 - }); - } - } - } else { - // 查询所有网络适配器 - let adapters_result: Vec> = wmi_con.query()?; - - for adapter in adapters_result { - // 从实例ID中提取VMID - let instance_id = adapter.get("InstanceID").and_then(variant_to_string).unwrap_or_default(); - let vmid = extract_uuid_from_instance_id(&instance_id); - - adapters.push(NetworkAdapter { - macaddress: adapter.get("Address").and_then(variant_to_string).unwrap_or_default(), - vmid, - switchname: adapter.get("SwitchName").and_then(variant_to_string).unwrap_or_default(), - ipaddresses: Vec::new(), - }); - } - } - - // 转换为JSON - let json = serde_json::to_string(&adapters)?; - Ok(json) -} - // 从实例ID中提取UUID fn extract_uuid_from_instance_id(instance_id: &str) -> String { let re_str = r"\{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}"; @@ -693,7 +791,7 @@ async fn get_network_handler(query: web::Query HttpResponse::Ok().body(result), Err(_) => HttpResponse::InternalServerError().json(serde_json::json!({ "error": "Failed to get network details" })), } @@ -838,20 +936,28 @@ async fn generate_vm_ticket(_vmid: &str) -> Result<(String, String)> { } let password: String = password_chars.into_iter().collect(); - // 创建临时用户并添加到Hyper-V管理员组,一分钟后自动删除 - let create_user_cmd = format!( - "New-LocalUser -Name '{}' -Password (ConvertTo-SecureString '{}' -AsPlainText -Force) -Description 'Temporary Hyper-V access'; Add-LocalGroupMember -Group 'Hyper-V Administrators' -Member '{}'; Start-Job -ScriptBlock {{ Start-Sleep -Seconds 60; Remove-LocalUser -Name '{}' }}", - username, password, username, username - ); + // 使用netuser_rs库创建临时用户并添加到Hyper-V管理员组 + let description = Some("Temporary Hyper-V access user".to_string()); - let output = Command::new("powershell.exe") - .args(["-Command", &create_user_cmd]) - .output()?; - - if !output.status.success() { - anyhow::bail!("Failed to create temporary user: {}", String::from_utf8_lossy(&output.stderr)); + // 创建用户 + if let Err(err) = netuser_rs::users::add_user(&username, &password, &description) { + anyhow::bail!("创建临时用户失败: {} - {}", err, netuser_rs::win_err_text(err)); } + // 添加到Hyper-V管理员组 + if let Err(err) = netuser_rs::groups::add_user_to_group(&username, "Hyper-V Administrators") { + // 如果添加到组失败,尝试删除用户 + let _ = netuser_rs::users::delete_user(&username); + anyhow::bail!("添加用户到Hyper-V管理员组失败: {} - {}", err, netuser_rs::win_err_text(err)); + } + + // 在后台启动线程60秒后删除用户 + let username_clone = username.clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_secs(60)); + let _ = netuser_rs::users::delete_user(&username_clone); + }); + Ok((username, password)) } @@ -897,7 +1003,7 @@ async fn main() -> std::io::Result<()> { .route("/api2/StopVM", web::post().to(stop_vm_handler)) .route("/api2/StartVM", web::post().to(start_vm_handler)) .route("/api2/ListVM", web::get().to(list_vm_handler)) - .route("/api2/getvmticket", web::get().to(get_vm_ticket_handler)) + .route("/api2/GetTicket", web::post().to(get_vm_ticket_handler)) }); let server = if tls_enabled {