Provisioning halfway :P
This commit is contained in:
Generated
+57
@@ -1669,6 +1669,32 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.2"
|
||||
@@ -1696,6 +1722,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"ssh2",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-dialog",
|
||||
@@ -2045,6 +2072,18 @@ version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
@@ -2837,6 +2876,18 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh2"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f84d13b3b8a0d4e91a2629911e951db1bb8671512f5c09d7d4ba34500ba68c8"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"libc",
|
||||
"libssh2-sys",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -3716,6 +3767,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version-compare"
|
||||
version = "0.2.1"
|
||||
|
||||
@@ -17,3 +17,4 @@ serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tauri-plugin-dialog = "2.7.1"
|
||||
tauri-plugin-fs = "2.5.1"
|
||||
ssh2 = "0.9"
|
||||
@@ -1,16 +1,211 @@
|
||||
use std::process::Command;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn remove_known_host(
|
||||
ip: String,
|
||||
) -> Result<String, String> {
|
||||
if ip.trim().is_empty() {
|
||||
return Err(
|
||||
"ip address cannot be empty"
|
||||
.into(),
|
||||
"ip address cannot be empty".into(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(format!(
|
||||
"removed stale known_hosts entry for {}",
|
||||
ip,
|
||||
let output = Command::new("ssh-keygen")
|
||||
.args(["-R", &ip])
|
||||
.output()
|
||||
.map_err(|error| {
|
||||
format!(
|
||||
"failed to run ssh-keygen: {}",
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(format!(
|
||||
"removed stale known_hosts entry for {}",
|
||||
ip,
|
||||
))
|
||||
} else {
|
||||
Err(String::from_utf8_lossy(&output.stderr)
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn probe_router_ssh(
|
||||
ip: String,
|
||||
) -> Result<String, String> {
|
||||
if ip.trim().is_empty() {
|
||||
return Err(
|
||||
"router IP is required".into(),
|
||||
);
|
||||
}
|
||||
|
||||
let output = Command::new("ssh")
|
||||
.args([
|
||||
"-o",
|
||||
"BatchMode=yes",
|
||||
"-o",
|
||||
"ConnectTimeout=5",
|
||||
"-o",
|
||||
"StrictHostKeyChecking=accept-new",
|
||||
&format!("root@{}", ip),
|
||||
"ubus call system board",
|
||||
])
|
||||
.output()
|
||||
.map_err(|error| {
|
||||
format!(
|
||||
"failed to run ssh command: {}",
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
|
||||
let stdout =
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
.to_string();
|
||||
|
||||
let stderr =
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
.to_string();
|
||||
|
||||
if output.status.success() {
|
||||
return Ok(stdout);
|
||||
}
|
||||
|
||||
if stderr.contains(
|
||||
"REMOTE HOST IDENTIFICATION HAS CHANGED",
|
||||
) {
|
||||
return Err(format!(
|
||||
"STALE_HOST_KEY: SSH host key mismatch for {}. Remove known_hosts entry and retry.",
|
||||
ip,
|
||||
));
|
||||
}
|
||||
|
||||
if stderr.contains("Permission denied")
|
||||
|| stderr.contains("publickey")
|
||||
|| stderr.contains("password")
|
||||
{
|
||||
return Err(format!(
|
||||
"AUTH_REQUIRED: Router is reachable at {}, but SSH requires password login. Try manually: ssh root@{} then run: ubus call system board",
|
||||
ip, ip,
|
||||
));
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"SSH_FAILED: {}\n{}",
|
||||
stderr, stdout,
|
||||
))
|
||||
}
|
||||
|
||||
use ssh2::Session;
|
||||
use std::{
|
||||
io::Read,
|
||||
net::{TcpStream, ToSocketAddrs},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn inspect_router_with_password(
|
||||
ip: String,
|
||||
password: String,
|
||||
) -> Result<String, String> {
|
||||
if ip.trim().is_empty() {
|
||||
return Err("router IP is required".into());
|
||||
}
|
||||
|
||||
if password.trim().is_empty() {
|
||||
return Err("router password is required".into());
|
||||
}
|
||||
|
||||
let address = format!("{}:22", ip);
|
||||
|
||||
let socket_address = address
|
||||
.to_socket_addrs()
|
||||
.map_err(|error| {
|
||||
format!("failed to resolve router address: {}", error)
|
||||
})?
|
||||
.next()
|
||||
.ok_or_else(|| {
|
||||
format!("failed to resolve router address {}", address)
|
||||
})?;
|
||||
|
||||
let tcp = TcpStream::connect_timeout(
|
||||
&socket_address,
|
||||
Duration::from_secs(5),
|
||||
)
|
||||
.map_err(|error| {
|
||||
format!("failed to connect to SSH on {}: {}", ip, error)
|
||||
})?;
|
||||
|
||||
tcp.set_read_timeout(Some(Duration::from_secs(10)))
|
||||
.map_err(|error| {
|
||||
format!("failed to set SSH read timeout: {}", error)
|
||||
})?;
|
||||
|
||||
tcp.set_write_timeout(Some(Duration::from_secs(10)))
|
||||
.map_err(|error| {
|
||||
format!("failed to set SSH write timeout: {}", error)
|
||||
})?;
|
||||
|
||||
let mut session = Session::new()
|
||||
.map_err(|error| {
|
||||
format!("failed to create SSH session: {}", error)
|
||||
})?;
|
||||
|
||||
session.set_tcp_stream(tcp);
|
||||
|
||||
session.handshake().map_err(|error| {
|
||||
format!("SSH handshake failed for {}: {}", ip, error)
|
||||
})?;
|
||||
|
||||
session
|
||||
.userauth_password("root", &password)
|
||||
.map_err(|error| {
|
||||
format!(
|
||||
"SSH authentication failed for root@{}: {}",
|
||||
ip, error
|
||||
)
|
||||
})?;
|
||||
|
||||
if !session.authenticated() {
|
||||
return Err(format!(
|
||||
"SSH authentication failed for root@{}",
|
||||
ip
|
||||
));
|
||||
}
|
||||
|
||||
let mut channel = session.channel_session().map_err(|error| {
|
||||
format!("failed to open SSH channel: {}", error)
|
||||
})?;
|
||||
|
||||
channel
|
||||
.exec("ubus call system board")
|
||||
.map_err(|error| {
|
||||
format!("failed to run router inspection command: {}", error)
|
||||
})?;
|
||||
|
||||
let mut stdout = String::new();
|
||||
|
||||
channel
|
||||
.read_to_string(&mut stdout)
|
||||
.map_err(|error| {
|
||||
format!("failed to read router inspection output: {}", error)
|
||||
})?;
|
||||
|
||||
channel.wait_close().map_err(|error| {
|
||||
format!("failed to close SSH channel: {}", error)
|
||||
})?;
|
||||
|
||||
let exit_status = channel.exit_status().map_err(|error| {
|
||||
format!("failed to read SSH command exit status: {}", error)
|
||||
})?;
|
||||
|
||||
if exit_status != 0 {
|
||||
return Err(format!(
|
||||
"router inspection command failed with exit code {}",
|
||||
exit_status
|
||||
));
|
||||
}
|
||||
|
||||
Ok(stdout)
|
||||
}
|
||||
@@ -13,7 +13,11 @@ use commands::{
|
||||
verify_router,
|
||||
wait_for_ssh,
|
||||
},
|
||||
ssh::remove_known_host,
|
||||
ssh::{
|
||||
inspect_router_with_password,
|
||||
probe_router_ssh,
|
||||
remove_known_host,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
@@ -25,6 +29,8 @@ pub fn run() {
|
||||
read_text_file,
|
||||
ping_host,
|
||||
remove_known_host,
|
||||
probe_router_ssh,
|
||||
inspect_router_with_password,
|
||||
detect_router,
|
||||
upload_firmware,
|
||||
flash_router,
|
||||
|
||||
Reference in New Issue
Block a user