This commit is contained in:
jiangcuo 2025-04-16 00:24:12 +08:00
parent 75fd783bc0
commit 7d1310a002
2 changed files with 309 additions and 202 deletions

View File

@ -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"}

View File

@ -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<String>,
ipv4_addresses: Vec<String>,
ipv6_addresses: Vec<String>,
fqdn: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
@ -151,7 +154,9 @@ struct NetworkAdapter {
macaddress: String,
vmid: String,
switchname: String,
ipaddresses: Vec<String>,
ipv4_addresses: Vec<String>,
ipv6_addresses: Vec<String>,
fqdn: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
@ -183,49 +188,40 @@ fn variant_to_string(v: &Variant) -> Option<String> {
}
async fn stop_vm(vmid: &str) -> Result<String> {
let wmi_con = get_wmi_connection()?;
// 查询虚拟机
let mut filters = HashMap::new();
filters.insert("Name".to_string(), FilterValue::String(vmid.to_string()));
let vms: Vec<HashMap<String, Variant>> = 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<String> {
let wmi_con = get_wmi_connection()?;
// 查询虚拟机
let mut filters = HashMap::new();
filters.insert("Name".to_string(), FilterValue::String(vmid.to_string()));
let vms: Vec<HashMap<String, Variant>> = 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<String> {
let wmi_con = get_wmi_connection()?;
// 查询特定虚拟机
// 查询虚拟机是否存在
let mut filters = HashMap::new();
filters.insert("Name".to_string(), FilterValue::String(vmid.to_string()));
let vms: Vec<HashMap<String, Variant>> = wmi_con.filtered_query(&filters)?;
@ -277,7 +273,7 @@ async fn list_vm() -> Result<String> {
// 获取内存大小
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<String> {
Ok(json)
}
// 获取虚拟机网络信息
fn get_vm_network_info(wmi_con: &WMIConnection, vm_id: &str) -> Result<IpInfo> {
println!("获取VM {} 的网络信息", vm_id);
fn get_vm_fqdn(wmi_con: &WMIConnection, vm_id: &str) -> Result<String> {
// 查询虚拟机的运行状态
let vm_query = format!("SELECT * FROM Msvm_ComputerSystem WHERE Name='{}'", vm_id);
let vms: Vec<HashMap<String, Variant>> = wmi_con.raw_query(&vm_query)?;
// 查询网络适配器
let net_query = format!("SELECT * FROM Msvm_SyntheticEthernetPortSettingData WHERE InstanceID LIKE '%{}%'", vm_id);
let network_adapters: Vec<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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("<PROPERTY NAME=\"Data\"") {
// 简单的XML解析提取Data元素的值
let data_start = xml_str.find("<PROPERTY NAME=\"Data\"");
if let Some(start_pos) = data_start {
let value_start = xml_str[start_pos..].find("<VALUE>");
let value_end = xml_str[start_pos..].find("</VALUE>");
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<String> {
let wmi_con = get_wmi_connection()?;
match vm_id {
Some(id) => {
let guest_info: Vec<HashMap<String, Variant>> = wmi_con.raw_query(&guestinfo_query)?;
// 查询网络适配器
let net_query = format!("SELECT * FROM Msvm_SyntheticEthernetPortSettingData WHERE InstanceID LIKE '%{}%'", id);
let network_adapters: Vec<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<u32> {
// 获取虚拟机内存大小(MB)
fn get_vm_memory(wmi_con: &WMIConnection, vm_id: &str) -> Result<u64> {
// 方法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<HashMap<String, Variant>> = 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<u64> {
}
}
// 方法3: 查询虚拟机配置
let query3 = format!(
"SELECT * FROM Msvm_VirtualSystemSettingData WHERE InstanceID LIKE '%{}%'",
vm_id
);
let vm_settings: Vec<HashMap<String, Variant>> = 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<String> {
Ok(json)
}
async fn get_network(vmid: Option<&str>) -> Result<String> {
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<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<HashMap<String, Variant>> = 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<std::collections::HashMap<String,
}
let vmid = query.get("VMID").map(|s| s.as_str());
match get_network(vmid).await {
match get_vm_network_info(vmid).await {
Ok(result) => 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 {