Support windows service, fix #143

This commit is contained in:
ssrlive 2024-09-14 09:55:27 +08:00
parent 187e251142
commit 6dadc1504a
5 changed files with 116 additions and 0 deletions

View file

@ -52,6 +52,9 @@ nix = { version = "0.29", default-features = false, features = [
android_logger = "0.14"
jni = { version = "0.21", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
windows-service = "0.7"
[build-dependencies]
serde_json = "1"

View file

@ -99,6 +99,11 @@ pub struct Args {
/// Verbosity level
#[arg(short, long, value_name = "level", value_enum, default_value = "info")]
pub verbosity: ArgVerbosity,
/// Daemonize the process as Windows service
#[cfg(target_os = "windows")]
#[arg(long)]
pub daemonize: bool,
}
fn validate_tun(p: &str) -> Result<String> {
@ -139,6 +144,8 @@ impl Default for Args {
udp_timeout: 10,
verbosity: ArgVerbosity::Info,
virtual_dns_pool: IpCidr::from_str("198.18.0.0/15").unwrap(),
#[cfg(target_os = "windows")]
daemonize: false,
}
}
}

View file

@ -5,6 +5,12 @@ async fn main() -> Result<(), BoxError> {
dotenvy::dotenv().ok();
let args = Args::parse_args();
#[cfg(target_os = "windows")]
if args.daemonize {
tun2proxy::win_svc::start_service()?;
return Ok(());
}
// let default = format!("{}={:?},trust_dns_proto=warn", module_path!(), args.verbosity);
let default = format!("{:?},trust_dns_proto=warn", args.verbosity);
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(default)).init();

View file

@ -60,6 +60,8 @@ pub mod socket_transfer;
mod socks;
mod traffic_status;
mod virtual_dns;
#[doc(hidden)]
pub mod win_svc;
const DNS_PORT: u16 = 53;

98
src/win_svc.rs Normal file
View file

@ -0,0 +1,98 @@
#![cfg(windows)]
const SERVICE_NAME: &str = "tun2proxy";
windows_service::define_windows_service!(ffi_service_main, my_service_main);
pub fn start_service() -> Result<(), windows_service::Error> {
// Register generated `ffi_service_main` with the system and start the service,
// blocking this thread until the service is stopped.
windows_service::service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
Ok(())
}
fn my_service_main(arguments: Vec<std::ffi::OsString>) {
// The entry point where execution will start on a background thread after a call to
// `service_dispatcher::start` from `main`.
if let Err(_e) = run_service(arguments) {
log::error!("Error: {:?}", _e);
}
}
fn run_service(_arguments: Vec<std::ffi::OsString>) -> Result<(), crate::BoxError> {
use windows_service::service::ServiceControl;
use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
let shutdown_token = crate::CancellationToken::new();
let shutdown_token_clone = shutdown_token.clone();
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
ServiceControl::Stop => {
// Handle stop event and return control back to the system.
shutdown_token_clone.cancel();
ServiceControlHandlerResult::NoError
}
// All services must accept Interrogate even if it's a no-op.
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
_ => ServiceControlHandlerResult::NotImplemented,
}
};
// Register system service event handler
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
let mut next_status = windows_service::service::ServiceStatus {
// Should match the one from system service registry
service_type: windows_service::service::ServiceType::OWN_PROCESS,
// The new state
current_state: windows_service::service::ServiceState::Running,
// Accept stop events when running
controls_accepted: windows_service::service::ServiceControlAccept::STOP,
// Used to report an error when starting or stopping only, otherwise must be zero
exit_code: windows_service::service::ServiceExitCode::Win32(0),
// Only used for pending states, otherwise must be zero
checkpoint: 0,
// Only used for pending states, otherwise must be zero
wait_hint: std::time::Duration::default(),
// Unused for setting status
process_id: None,
};
// Tell the system that the service is running now
status_handle.set_service_status(next_status.clone())?;
let args = crate::Args::parse_args();
let default = format!("{:?},trust_dns_proto=warn", args.verbosity);
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(default)).init();
let join_handle = tokio::spawn({
async move {
unsafe extern "C" fn traffic_cb(status: *const crate::TrafficStatus, _: *mut std::ffi::c_void) {
let status = &*status;
log::debug!("Traffic: ▲ {} : ▼ {}", status.tx, status.rx);
}
unsafe { crate::tun2proxy_set_traffic_status_callback(1, Some(traffic_cb), std::ptr::null_mut()) };
if let Err(err) = crate::desktop_run_async(args, shutdown_token).await {
log::error!("main loop error: {}", err);
}
}
});
let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
rt.block_on(async {
if let Err(err) = join_handle.await {
log::error!("main_entry error {}", err);
}
Ok::<(), crate::Error>(())
})?;
// Tell the system that the service is stopped now
next_status.current_state = windows_service::service::ServiceState::Stopped;
status_handle.set_service_status(next_status)?;
Ok(())
}