feat: add Windows firewall management for openlist port #49

This commit is contained in:
Kuingsmile
2025-07-17 14:03:40 +08:00
parent 0231fa20d7
commit a19e74ce1f
9 changed files with 295 additions and 6 deletions

View File

@@ -0,0 +1,120 @@
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
use std::process::{Command, ExitStatus};
#[cfg(target_os = "windows")]
use deelevate::{PrivilegeLevel, Token};
#[cfg(target_os = "windows")]
use runas::Command as RunasCommand;
use tauri::State;
use crate::object::structs::AppState;
#[cfg(target_os = "windows")]
const RULE: &str = "OpenList Core";
#[cfg(target_os = "windows")]
fn netsh_with_elevation(args: &[String]) -> Result<ExitStatus, String> {
let token = Token::with_current_process().map_err(|e| format!("token: {e}"))?;
let elevated = !matches!(
token
.privilege_level()
.map_err(|e| format!("privilege: {e}"))?,
PrivilegeLevel::NotPrivileged
);
if elevated {
Command::new("netsh")
.args(args)
.creation_flags(0x08000000)
.status()
} else {
RunasCommand::new("netsh").args(args).show(false).status()
}
.map_err(|e| format!("netsh: {e}"))
}
#[cfg(target_os = "windows")]
fn firewall_rule(verb: &str, port: Option<u16>) -> Result<bool, String> {
let mut args: Vec<String> = vec![
"advfirewall".into(),
"firewall".into(),
verb.into(),
"rule".into(),
format!("name={RULE}"),
];
if let Some(p) = port {
args.extend([
"dir=in".into(),
"action=allow".into(),
"protocol=TCP".into(),
format!("localport={p}"),
"description=Allow OpenList Core web interface access".into(),
]);
}
Ok(netsh_with_elevation(&args)?.success())
}
#[cfg(target_os = "windows")]
fn rule_stdout() -> Result<Option<String>, String> {
let out = Command::new("netsh")
.args([
"advfirewall",
"firewall",
"show",
"rule",
&format!("name={RULE}"),
])
.output()
.map_err(|e| format!("netsh: {e}"))?;
if out.status.success() {
Ok(Some(String::from_utf8_lossy(&out.stdout).into()))
} else {
Ok(None)
}
}
#[cfg(not(target_os = "windows"))]
fn firewall_rule(_: &str, _: Option<u16>) -> Result<bool, String> {
Ok(true)
}
#[cfg(not(target_os = "windows"))]
fn rule_stdout() -> Result<Option<String>, String> {
Ok(None)
}
#[tauri::command]
pub async fn check_firewall_rule(state: State<'_, AppState>) -> Result<bool, String> {
let port = state
.app_settings
.read()
.clone()
.ok_or("read settings")?
.openlist
.port;
if let Some(out) = rule_stdout()? {
Ok(out.contains(&port.to_string()))
} else {
Ok(false)
}
}
#[tauri::command]
pub async fn add_firewall_rule(state: State<'_, AppState>) -> Result<bool, String> {
let port = state
.app_settings
.read()
.clone()
.ok_or("read settings")?
.openlist
.port;
let _ = firewall_rule("delete", None);
firewall_rule("add", Some(port))
}
#[tauri::command]
pub async fn remove_firewall_rule(_state: State<'_, AppState>) -> Result<bool, String> {
firewall_rule("delete", None)
}

View File

@@ -1,6 +1,7 @@
pub mod binary;
pub mod config;
pub mod custom_updater;
pub mod firewall;
pub mod http_api;
pub mod logs;
pub mod openlist_core;

View File

@@ -13,6 +13,7 @@ use cmd::custom_updater::{
check_for_updates, download_update, get_current_version, install_update_and_restart,
is_auto_check_enabled, set_auto_check_enabled,
};
use cmd::firewall::{add_firewall_rule, check_firewall_rule, remove_firewall_rule};
use cmd::http_api::{
delete_process, get_process_list, restart_process, start_process, stop_process, update_process,
};
@@ -159,6 +160,9 @@ pub fn run() {
check_service_status,
stop_service,
start_service,
check_firewall_rule,
add_firewall_rule,
remove_firewall_rule,
check_for_updates,
download_update,
install_update_and_restart,