Sumber yang diterbitkan
Muat turun ZIP sumber
CH-J Tools
Layari direktori dan fail untuk keluaran aplikasi tertentu.
CH-J / Server Manager
872 fail
core-20261030T220507Z-61e4558d889f
Ditambah: · Dibina:
1
//! Remote OS detection from system facts (port of `sessions/platformDetect.js`):2
//! `uname -s` for POSIX, otherwise an encoded PowerShell probe; never prompts or host names.4
use crate::core::error::CoreResult;5
use crate::core::ssh::session::{SessionManager, SessionRecord};6
use crate::core::ssh::windows_exec::{encoded_command, parse_windows_output};7
use once_cell::sync::Lazy;8
use regex::Regex;9
use serde_json::{json, Value};10
use std::sync::Arc;11
use std::time::Duration;13
static POSIX_KERNELS: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(Linux|Darwin|FreeBSD|OpenBSD|NetBSD|DragonFly|SunOS|AIX)\b").unwrap());15
pub const WINDOWS_PROBE: &str = r#"$ProgressPreference='SilentlyContinue'16
try {17
# Registry first: standard accounts on an SSH network logon are usually denied WMI/CIM access.18
$nt = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -ErrorAction SilentlyContinue19
$shell = (Get-ItemProperty 'HKLM:\SOFTWARE\OpenSSH' -ErrorAction SilentlyContinue).DefaultShell20
$caption = [string]$nt.ProductName; $build = [string]$nt.CurrentBuildNumber21
if ($caption -match '^Windows 10' -and [int]("0$build") -ge 22000) { $caption = $caption -replace '^Windows 10', 'Windows 11' }22
$type = [string](Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\ProductOptions' -ErrorAction SilentlyContinue).ProductType23
$productType = @{ 'WinNT' = 1; 'LanmanNT' = 2; 'ServerNT' = 3 }[$type]24
$version = [Environment]::OSVersion.Version.ToString()25
try { $os = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop; $caption = $os.Caption; $version = $os.Version; $productType = [int]$os.ProductType } catch {}26
$value = [ordered]@{ caption = $caption; version = $version; build = $build; productType = $productType27
installationType = [string]$nt.InstallationType; defaultShell = [string]$shell; psVersion = $PSVersionTable.PSVersion.ToString()28
is64BitProcess = [Environment]::Is64BitProcess; languageMode = [string]$ExecutionContext.SessionState.LanguageMode29
localAccounts = [bool](Get-Command Get-LocalUser -ErrorAction SilentlyContinue); pwsh = [string](Get-Command pwsh.exe -ErrorAction SilentlyContinue).Source }30
[Console]::Out.WriteLine('CHJ_RESULT:' + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($value | ConvertTo-Json -Compress))))31
} catch { [Console]::Out.WriteLine('CHJ_ERR:PLATFORM_PROBE_FAILED:' + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($_.Exception.Message))) }"#;33
pub fn shell_name(default_shell: &str) -> String {34
let value = default_shell.to_lowercase();35
if value.is_empty() {36
return "cmd.exe".into();37
}38
for (pattern, name) in [(r"pwsh(\.exe)?$", "pwsh"), (r"powershell(\.exe)?$", "powershell"), (r"cmd(\.exe)?$", "cmd.exe"), (r"bash(\.exe)?$", "bash")] {39
if Regex::new(pattern).unwrap().is_match(&value) {40
return name.into();41
}42
}43
value.rsplit(['\\', '/']).next().unwrap_or("").to_string()44
}46
pub async fn detect_platform(manager: &SessionManager, record: &Arc<SessionRecord>, refresh: bool) -> CoreResult<Value> {47
if !refresh {48
if let Some(platform) = record.platform.lock().clone() {49
return Ok(platform);50
}51
}52
let uname = match manager.exec_channel(record, "uname -s", Duration::from_secs(15), 512 * 1024).await {53
Ok(mut channel) => {54
channel.eof().await;55
channel.finish().await.ok()56
}57
Err(error) if error.code == "SESSION_NOT_CONNECTED" => return Err(error),58
Err(_) => None,59
};60
let kernel = uname.as_ref().map(|r| r.stdout.trim().lines().next().unwrap_or("").to_string()).unwrap_or_default();61
if uname.as_ref().map(|r| r.code == Some(0)).unwrap_or(false) && POSIX_KERNELS.is_match(&kernel) {62
let platform = json!({ "os": if kernel.starts_with("Linux") { "linux" } else { "posix" }, "kernel": kernel, "detectedBy": "uname" });63
*record.platform.lock() = Some(platform.clone());64
return Ok(platform);65
}66
let mut channel = manager.exec_channel(record, &encoded_command(WINDOWS_PROBE), Duration::from_secs(60), 512 * 1024).await?;67
channel.eof().await;68
let probe = channel.finish().await?;69
let platform = match parse_windows_output(&probe.stdout, &probe.stderr, probe.code, "PLATFORM_") {70
Err(error) => json!({ "os": "unknown", "kernel": kernel, "detectedBy": "none", "reason": error.message }),71
Ok(info) => {72
let text = |k: &str| info.get(k).map(|v| v.as_str().map(str::to_string).unwrap_or_else(|| if v.is_null() { String::new() } else { v.to_string() })).unwrap_or_default();73
json!({74
"os": "windows",75
"detectedBy": "Win32_OperatingSystem",76
"caption": text("caption"),77
"version": text("version"),78
"build": text("build"),79
"server": info.get("productType").and_then(Value::as_f64) != Some(1.0),80
"serverCore": Regex::new(r"(?i)server core").unwrap().is_match(&text("installationType")),81
"installationType": text("installationType"),82
"defaultShell": shell_name(&text("defaultShell")),83
"defaultShellPath": text("defaultShell"),84
"powershell": text("psVersion"),85
"is64BitProcess": info.get("is64BitProcess") == Some(&Value::Bool(true)),86
"languageMode": text("languageMode"),87
"localAccounts": info.get("localAccounts") == Some(&Value::Bool(true)),88
"pwsh": text("pwsh"),89
"unameOutput": kernel90
})91
}92
};93
*record.platform.lock() = Some(platform.clone());94
Ok(platform)95
}97
#[cfg(test)]98
mod tests {99
use super::*;101
#[test]102
fn shells() {103
assert_eq!(shell_name(""), "cmd.exe");104
assert_eq!(shell_name("C:\\Program Files\\PowerShell\\7\\pwsh.exe"), "pwsh");105
assert_eq!(shell_name("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"), "powershell");106
assert_eq!(shell_name("C:\\tools\\fish.exe"), "fish.exe");107
}108
}SHA-256
a9cb33677591db4e34857559eae3df235aa095cd4cb4220c33d0023d1cee8d3aCH-J Proprietary Software License 1.14 · Kod sumber disediakan di bawah CH-J Proprietary Software License 1.14. Ketersediaannya tidak mengubah terma lesen atau memberikan hak tambahan.
SHA-256 arkib
77380d4805485ffa4027fbe7858b5836f197a4aa9d52358a43e2d12d018744df