summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock20
-rw-r--r--Cargo.toml4
-rw-r--r--README.md60
-rw-r--r--WINDOWS_GUIDE.txt39
-rw-r--r--dpibreak.1.in22
-rw-r--r--dpibreak.1.md13
-rw-r--r--res/windows_bats/service_install.bat27
-rw-r--r--res/windows_bats/service_remove.bat22
-rw-r--r--src/main.rs6
-rw-r--r--src/opt.rs178
-rw-r--r--src/platform/linux.rs36
-rw-r--r--src/platform/windows.rs46
12 files changed, 362 insertions, 111 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ce59a07..ac8103d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -235,6 +235,15 @@ dependencies = [
]
[[package]]
+name = "daemonize"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "dispatch2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -253,12 +262,14 @@ dependencies = [
"anyhow",
"criterion",
"ctrlc",
+ "daemonize",
"etherparse 0.18.2",
"nfq-updated",
"nix 0.27.1",
"serde_json",
"socket2",
"windivert",
+ "windows-services",
"winres",
]
@@ -780,6 +791,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
+name = "windows-services"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cffa4f5d8d05f500769005f4fa875e9b50059c8c6c84cc5dc810848d895f8ba9"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 0082d0a..25081c4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,9 +38,11 @@ nfq = { package = "nfq-updated", version = "0.2.6" } # nfq-updated: to us as_raw
socket2 = { version = "0.6", features = ["all"] }
nix = { version = "0.27.1", features = ["fs", "poll"] }
serde_json = "1.0.145"
+daemonize = "0.5.0"
[target.'cfg(windows)'.dependencies]
windivert = { version = "0.6.0" }
+windows-services = "0.26"
[build-dependencies]
winres = "0.1"
@@ -61,4 +63,4 @@ bench = []
[[bench]]
name = "hoptab_bench"
harness = false
-required-features = ["bench"] \ No newline at end of file
+required-features = ["bench"]
diff --git a/README.md b/README.md
index 83f7233..d2304f7 100644
--- a/README.md
+++ b/README.md
@@ -35,29 +35,32 @@ section on [dpibreak(1)](./dpibreak.1.md#OPTIONS) for more
information.)
## Quickstart
+Download the release from
+<https://github.com/dilluti0n/dpibreak/releases/latest>.
### Windows
-1. Download and unzip the release from
- <https://github.com/dilluti0n/dpibreak/releases/latest>.
-2. Double-click `dpibreak.exe` or `start_fake.bat` to use
-[fake](#fake). (See `WINDOWS_GUIDE.txt` for more information.)
+- Double-click `dpibreak.exe` or `start_fake.bat` (To use
+[fake](#fake)).
+- Run `service_install.bat` with administrator privileges to
+ automatically run per boot (To remove, run `service_remove.bat`).
+- See `WINDOWS_GUIDE.txt` for more information.
### Linux
-1. Download release tarball from
- <https://github.com/dilluti0n/dpibreak/releases/latest>.
-2. Extract and install:
+1. Extract and install:
```bash
tar -xf DPIBreak-X.Y.Z-x86_64-unknown-linux-musl.tar.gz
cd DPIBreak-X.Y.Z-x86_64-unknown-linux-musl
sudo make install
```
-3. To run:
+2. To run:
```bash
sudo dpibreak
-sudo dpibreak --fake --fake-ttl 8
+sudo dpibreak -D # run as daemon
+sudo dpibreak --fake-autottl # enable fake packet injection
+sudo dpibreak -D --fake-autottl
dpibreak --help
-man 1 dpibreak # manual
+man 1 dpibreak # manual
```
-4. To uninstall:
+3. To uninstall:
```bash
sudo make uninstall
```
@@ -67,16 +70,13 @@ Available in the [GURU](https://wiki.gentoo.org/wiki/Project:GURU)
repository.
```bash
-eselect repository enable guru
-emaint sync -r guru
-echo net-misc/dpibreak ~amd64 > /etc/portage/package.accept_keywords/dpibreak
-emerge --ask net-misc/dpibreak
+sudo eselect repository enable guru
+sudo emaint sync -r guru
+echo 'net-misc/dpibreak ~amd64' | sudo tee /etc/portage/package.accept_keywords/dpibreak
+sudo emerge --ask net-misc/dpibreak
```
-### Linux with Cargo (crates.io)
-Install Rust toolchain from
-<https://www.rust-lang.org/learn/get-started>.
-
+### For rust developers (crates.io)
```bash
cargo install dpibreak
```
@@ -145,9 +145,17 @@ For introducing the circumvention idea:
- [zapret](https://github.com/bol-van/zapret) by bol-van
- [SpoofDPI](https://github.com/xvzc/SpoofDPI) by xzvc
-## See more
+## Notice
+Copyright 2025-2026 Dilluti0n.
+
+Licensed under GPL-3.0-or-later.
+
+![License-logo](./res/gplv3-with-text-136x68.png)
+
+<details>
+<summary>See more (alternative tools & useful links)</summary>
-Alternative tools:
+#### Alternative tools:
- [Green Tunnel](https://github.com/SadeghHayeri/GreenTunnel) by
SadeghHayeri (for MacOS, Linux and Windows)
- [DPI Tunnel CLI](https://github.com/nomoresat/DPITunnel-cli) by
@@ -170,16 +178,10 @@ Alternative tools:
Waujito (for OpenWRT/Entware routers and Linux)
- [NoDPI](https://github.com/GVCoder09/NoDPI/) for Windows and Linux
-Useful links:
+#### Useful links:
- <https://geneva.cs.umd.edu/papers/geneva_ccs19.pdf>
- <https://github.com/bol-van/zapret/blob/master/docs/readme.en.md>
- <https://deepwiki.com/bol-van/zapret/3-dpi-circumvention-techniques>
- <https://www.ias.edu/security/deep-packet-inspection-dead-and-heres-why>
- <https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/>
-
-## Notice
-Copyright 2025-2026 Dilluti0n.
-
-Licensed under GPL-3.0-or-later.
-
-![License-logo](./res/gplv3-with-text-136x68.png)
+</details>
diff --git a/WINDOWS_GUIDE.txt b/WINDOWS_GUIDE.txt
index efd9afa..91fb971 100644
--- a/WINDOWS_GUIDE.txt
+++ b/WINDOWS_GUIDE.txt
@@ -1,9 +1,36 @@
-DPIBreak Windows users' guide
+DPIBreak Windows user guide
+===========================
-- Double click dpibreak.exe to run with normal mode.
-- Double click start_fake.bat to run with fake mode (see --fake part
- of manual).
-- After it starts, a console window will open. Keep this window open
+See dpibreak.1.md for full manual.
+
+Normal start:
+* Double click dpibreak.exe to run with normal mode.
+* Double click start_fake.bat to run with fake mode.
+ - See `--fake' of manual for more information.
+* After it starts, a console window will open. Keep this window open
while using the program.
-- See dpibreak.1.md for full manual.
+Windows service (automatically start at boot):
+* Install & start: Right click service_install.bat -> Run as administrator
+ - DPIBreak will automatically start per boot
+ - See `-D, --daemon' of manual for more information.
+* Uninstall: Right click service_remove.bat -> Run as administrator
+
+---
+
+DPIBreak Windows 사용 안내
+=========================
+
+전체 설명서는 dpibreak.1.md를 참고하세요.
+
+일반 실행:
+* dpibreak.exe를 더블 클릭하면 일반 모드로 실행됩니다.
+* start_fake.bat을 더블 클릭하면 fake 모드로 실행됩니다.
+ - 자세한 내용은 설명서의 `--fake' 항목을 참고하세요.
+* 실행 후 콘솔 창이 열립니다. 프로그램 사용 중에는 이 창을 닫지 마세요.
+
+Windows 서비스 (부팅 시 자동 실행):
+* 설치 및 시작: service_install.bat 우클릭 -> 관리자 권한으로 실행
+ - 부팅 시 DPIBreak가 자동으로 시작됩니다.
+ - 자세한 내용은 설명서의 `-D, --daemon' 항목을 참고하세요.
+* 제거: service_remove.bat 우클릭 -> 관리자 권한으로 실행
diff --git a/dpibreak.1.in b/dpibreak.1.in
index 0c2a7da..b62cae5 100644
--- a/dpibreak.1.in
+++ b/dpibreak.1.in
@@ -58,6 +58,20 @@ startup.
.SH OPTIONS
.TP
+.BR \-D ", " \-\-daemon
+Run as a background daemon. Logs are written to
+.B /tmp/dpibreak.log.
+If a daemon is already running, it will fail with "unable to lock pid
+file". To stop it, run
+.B kill \(gacat /tmp/dpibreak.pid\(ga
+as root.
+.RS
+.PP
+On Windows, this option enters the service controller entry
+point. Example:
+.B sc create dpibreak binPath= \(dqdpibreak.exe \-D\(dq start= auto; sc start dpibreak
+.RE
+.TP
.B \-\-delay\-ms \fI<u64>\fR
Delay in milliseconds to apply between fragmented pieces of the
ClientHello. Typical values are 0–1000; larger values may increase
@@ -131,6 +145,14 @@ Run with default options:
.B dpibreak
.RE
.PP
+Run as daemon with
+.B fake
+feature:
+.PP
+.RS
+.B dpibreak \-D \-\-fake\-autottl
+.RE
+.PP
Run with a 10 ms delay and verbose logging:
.PP
.RS
diff --git a/dpibreak.1.md b/dpibreak.1.md
index 6d528fa..f5760b2 100644
--- a/dpibreak.1.md
+++ b/dpibreak.1.md
@@ -40,6 +40,15 @@ program opens the driver automatically at startup.
## OPTIONS
+**-D**, **--daemon**
+Run as a background daemon. Logs are written to **/tmp/dpibreak.log.**
+If a daemon is already running, it will fail with "unable to lock pid
+file". To stop it, run **kill \`cat /tmp/dpibreak.pid\`** as root.
+
+On Windows, this option enters the service controller entry point.
+Example: **sc create dpibreak binPath= "dpibreak.exe -D" start= auto; sc
+start dpibreak**
+
**--delay-ms *\<u64\>***
Delay in milliseconds to apply between fragmented pieces of the
ClientHello. Typical values are 0–1000; larger values may increase
@@ -96,6 +105,10 @@ Run with default options:
> **dpibreak**
+Run as daemon with **fake** feature:
+
+> **dpibreak -D --fake-autottl**
+
Run with a 10 ms delay and verbose logging:
> **dpibreak --delay-ms 10 --loglevel debug**
diff --git a/res/windows_bats/service_install.bat b/res/windows_bats/service_install.bat
new file mode 100644
index 0000000..327f37e
--- /dev/null
+++ b/res/windows_bats/service_install.bat
@@ -0,0 +1,27 @@
+@REM SPDX-FileCopyrightText: 2026 Dilluti0n <[email protected]>
+@REM SPDX-License-Identifier: GPL-3.0-or-later
+
+@echo Please run this script with administrator privileges.
+@echo Right click, select "Run as administrator".
+@echo.
+@echo Press any key if you are running this as administrator.
+@pause
+
+@set "ARGS=-D --fake-ttl 8 --fake-autottl"
+@set "EXE=%~dp0dpibreak.exe"
+@set "SERVNAME=dpibreak"
+
+sc stop %SERVNAME%
+sc delete %SERVNAME%
+sc create %SERVNAME% binPath= "\"%EXE%\" %ARGS%" start= auto
+sc description %SERVNAME% "Simple and efficient DPI circumvention tool in Rust."
+sc start %SERVNAME%
+@if %ERRORLEVEL% equ 0 (
+ @echo.
+ @echo Installation complete. %SERVNAME% is running and will start automatically on boot.
+ @echo To stop and uninstall the service, run service_remove.bat.
+) else (
+ @echo.
+ @echo Installation failed. Make sure to run this script as administrator.
+)
+@pause
diff --git a/res/windows_bats/service_remove.bat b/res/windows_bats/service_remove.bat
new file mode 100644
index 0000000..1eb8183
--- /dev/null
+++ b/res/windows_bats/service_remove.bat
@@ -0,0 +1,22 @@
+@REM SPDX-FileCopyrightText: 2026 Dilluti0n <[email protected]>
+@REM SPDX-License-Identifier: GPL-3.0-or-later
+
+@echo Please run this script with administrator privileges.
+@echo Right click, select "Run as administrator".
+@echo.
+@echo Press any key if you are running this as administrator.
+@pause
+
+@set "SERVNAME=dpibreak"
+
+sc stop %SERVNAME%
+sc delete %SERVNAME%
+@if %ERRORLEVEL% equ 0 (
+ @echo.
+ @echo Service %SERVNAME% removed successfully.
+ @sc stop windivert >nul 2>&1
+) else (
+ @echo.
+ @echo Service %SERVNAME% not found or already removed.
+)
+@pause
diff --git a/src/main.rs b/src/main.rs
index a36bd48..90efdcc 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -64,7 +64,11 @@ fn splash_banner() {
fn main_0() -> Result<()> {
trap_exit()?;
- opt::parse_args();
+ let opt = opt::Opt::from_args()?;
+ if opt.daemon {
+ platform::daemonize_1();
+ }
+ opt.set_opt()?;
splash_banner();
let _guard = EnsureCleanup;
diff --git a/src/opt.rs b/src/opt.rs
index 7d962a3..196420e 100644
--- a/src/opt.rs
+++ b/src/opt.rs
@@ -23,6 +23,7 @@ static OPT_DELAY_MS: OnceLock<u64> = OnceLock::new();
#[cfg(target_os = "linux")] static OPT_QUEUE_NUM: OnceLock<u16> = OnceLock::new();
#[cfg(target_os = "linux")] static OPT_NFT_COMMAND: OnceLock<String> = OnceLock::new();
+const DEFAULT_DAEMON: bool = false;
#[cfg(debug_assertions)] const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug;
#[cfg(not(debug_assertions))] const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Warning;
const DEFAULT_NO_SPLASH: bool = false;
@@ -37,6 +38,109 @@ const DEFAULT_DELAY_MS: u64 = 0;
#[cfg(target_os = "linux")] const DEFAULT_QUEUE_NUM: u16 = 1;
#[cfg(target_os = "linux")] const DEFAULT_NFT_COMMAND: &str = "nft";
+pub struct Opt {
+ pub daemon: bool,
+ log_level: LogLevel,
+ no_splash: bool,
+ fake: bool,
+ fake_ttl: u8,
+ fake_autottl: bool,
+ fake_badsum: bool,
+ delay_ms: u64,
+ #[cfg(target_os = "linux")] queue_num: u16,
+ #[cfg(target_os = "linux")] nft_command: String,
+}
+
+impl Opt {
+ pub fn from_args() -> Result<Self> {
+ let mut daemon = DEFAULT_DAEMON;
+ let mut log_level = DEFAULT_LOG_LEVEL;
+ let mut delay_ms = DEFAULT_DELAY_MS;
+ let mut no_splash = DEFAULT_NO_SPLASH;
+ let mut fake = DEFAULT_FAKE;
+ let mut fake_ttl = DEFAULT_FAKE_TTL;
+ let mut fake_autottl = DEFAULT_FAKE_AUTOTTL;
+ let mut fake_badsum = DEFAULT_FAKE_BADSUM;
+
+ #[cfg(target_os = "linux")]
+ let mut queue_num: u16 = DEFAULT_QUEUE_NUM;
+ #[cfg(target_os = "linux")]
+ let mut nft_command = String::from(DEFAULT_NFT_COMMAND);
+
+ let mut args = std::env::args().skip(1); // program name
+
+ let mut warned_loglevel_deprecated = false;
+
+ while let Some(arg) = args.next() {
+ let argv = arg.as_str();
+
+ match argv {
+ "-h" | "--help" => { usage(); std::process::exit(0); }
+ "-D" | "--daemon" => {
+ no_splash = true;
+ // if it is unchanged explicitly by argument, set it to info
+ if log_level == DEFAULT_LOG_LEVEL {
+ log_level = LogLevel::Info;
+ }
+ daemon = true;
+ }
+ "--delay-ms" => { delay_ms = take_value(&mut args, argv)?; }
+ "--log-level" | "--loglevel" => {
+ if argv == "--loglevel" && !warned_loglevel_deprecated {
+ // FIXME(on release): remove this on v1.0.0
+ warned_loglevel_deprecated = true;
+ eprintln!("Note: `{arg}' has been deprecated since v0.1.1. Use `--log-level' instead.");
+ }
+ log_level = take_value(&mut args, argv)?;
+ }
+ "--no-splash" => { no_splash = true; }
+
+ "--fake" => { fake = true; }
+ "--fake-ttl" => { fake = true; fake_ttl = take_value(&mut args, argv)?; }
+ "--fake-autottl" => { fake = true; fake_autottl = true }
+ "--fake-badsum" => { fake = true; fake_badsum = true }
+
+ #[cfg(target_os = "linux")]
+ "--queue-num" => { queue_num = take_value(&mut args, argv)?; }
+
+ #[cfg(target_os = "linux")]
+ "--nft-command" => { nft_command = take_value(&mut args, argv)?; }
+
+ _ => { return Err(anyhow!("argument: unknown: {}", arg)); }
+ }
+ }
+
+ Ok(Opt {
+ daemon: daemon,
+ log_level: log_level,
+ no_splash: no_splash,
+ fake: fake,
+ fake_ttl: fake_ttl,
+ fake_autottl: fake_autottl,
+ fake_badsum: fake_badsum,
+ delay_ms: delay_ms,
+ #[cfg(target_os = "linux")] queue_num: queue_num,
+ #[cfg(target_os = "linux")] nft_command: nft_command,
+ })
+ }
+
+ pub fn set_opt(self) -> Result<()> {
+ set_opt("OPT_LOG_LEVEL", &OPT_LOG_LEVEL, self.log_level)?;
+ set_opt("OPT_NO_SPLASH", &OPT_NO_SPLASH, self.no_splash)?;
+
+ set_opt("OPT_DELAY_MS", &OPT_DELAY_MS, self.delay_ms)?;
+ set_opt("OPT_FAKE", &OPT_FAKE, self.fake)?;
+ set_opt("OPT_FAKE_TTL", &OPT_FAKE_TTL, self.fake_ttl)?;
+ set_opt("OPT_FAKE_AUTOTTL", &OPT_FAKE_AUTOTTL, self.fake_autottl)?;
+ set_opt("OPT_FAKE_BADSUM", &OPT_FAKE_BADSUM, self.fake_badsum)?;
+
+ #[cfg(target_os = "linux")] set_opt("OPT_QUEUE_NUM", &OPT_QUEUE_NUM, self.queue_num)?;
+ #[cfg(target_os = "linux")] set_opt("OPT_NFT_COMMAND", &OPT_NFT_COMMAND, self.nft_command)?;
+
+ Ok(())
+ }
+}
+
pub fn no_splash() -> bool {
*OPT_NO_SPLASH.get().unwrap_or(&DEFAULT_NO_SPLASH)
}
@@ -91,6 +195,7 @@ where
fn usage() {
println!("Usage: dpibreak [OPTIONS]\n");
println!("Options:");
+ println!(" -D, --daemon Run as daemon; kill `pidof dpibreak` to stop.");
println!(" --delay-ms <u64> (default: {DEFAULT_DELAY_MS})");
#[cfg(target_os = "linux")]
println!(" --queue-num <u16> (default: {DEFAULT_QUEUE_NUM})");
@@ -121,76 +226,3 @@ fn set_opt<T: std::fmt::Display>(
Ok(())
}
-
-fn parse_args_1() -> Result<()> {
- let mut log_level = DEFAULT_LOG_LEVEL;
- let mut delay_ms = DEFAULT_DELAY_MS;
- let mut no_splash = DEFAULT_NO_SPLASH;
- let mut fake = DEFAULT_FAKE;
- let mut fake_ttl = DEFAULT_FAKE_TTL;
- let mut fake_autottl = DEFAULT_FAKE_AUTOTTL;
- let mut fake_badsum = DEFAULT_FAKE_BADSUM;
-
- #[cfg(target_os = "linux")]
- let mut queue_num: u16 = DEFAULT_QUEUE_NUM;
- #[cfg(target_os = "linux")]
- let mut nft_command = String::from(DEFAULT_NFT_COMMAND);
-
- let mut args = std::env::args().skip(1); // program name
-
- let mut warned_loglevel_deprecated = false;
-
- while let Some(arg) = args.next() {
- let argv = arg.as_str();
-
- match argv {
- "-h" | "--help" => { usage(); std::process::exit(0); }
- "--delay-ms" => { delay_ms = take_value(&mut args, argv)?; }
- "--log-level" | "--loglevel" => {
- if argv == "--loglevel" && !warned_loglevel_deprecated {
- // FIXME(on release): remove this on v1.0.0
- warned_loglevel_deprecated = true;
- eprintln!("Note: `{arg}' has been deprecated since v0.1.1. \
-Use `--log-level' instead.");
- }
- log_level = take_value(&mut args, argv)?;
- }
- "--no-splash" => { no_splash = true; }
-
- "--fake" => { fake = true; }
- "--fake-ttl" => { fake = true; fake_ttl = take_value(&mut args, argv)?; }
- "--fake-autottl" => { fake = true; fake_autottl = true }
- "--fake-badsum" => { fake = true; fake_badsum = true }
-
- #[cfg(target_os = "linux")]
- "--queue-num" => { queue_num = take_value(&mut args, argv)?; }
-
- #[cfg(target_os = "linux")]
- "--nft-command" => { nft_command = take_value(&mut args, argv)?; }
-
- _ => { return Err(anyhow!("argument: unknown: {}", arg)); }
- }
- }
-
- set_opt("OPT_LOG_LEVEL", &OPT_LOG_LEVEL, log_level)?;
- set_opt("OPT_NO_SPLASH", &OPT_NO_SPLASH, no_splash)?;
-
- set_opt("OPT_DELAY_MS", &OPT_DELAY_MS, delay_ms)?;
- set_opt("OPT_FAKE", &OPT_FAKE, fake)?;
- set_opt("OPT_FAKE_TTL", &OPT_FAKE_TTL, fake_ttl)?;
- set_opt("OPT_FAKE_AUTOTTL", &OPT_FAKE_AUTOTTL, fake_autottl)?;
- set_opt("OPT_FAKE_BADSUM", &OPT_FAKE_BADSUM, fake_badsum)?;
-
- #[cfg(target_os = "linux")] set_opt("OPT_QUEUE_NUM", &OPT_QUEUE_NUM, queue_num)?;
- #[cfg(target_os = "linux")] set_opt("OPT_NFT_COMMAND", &OPT_NFT_COMMAND, nft_command)?;
-
- Ok(())
-}
-
-pub fn parse_args() {
- if let Err(e) = parse_args_1() {
- log_println!(LogLevel::Error, "{e}");
- usage();
- std::process::exit(1);
- }
-}
diff --git a/src/platform/linux.rs b/src/platform/linux.rs
index 32fd819..e6cf577 100644
--- a/src/platform/linux.rs
+++ b/src/platform/linux.rs
@@ -220,3 +220,39 @@ pub fn run() -> Result<()> {
Ok(())
}
+
+const PKG_NAME: &str = env!("CARGO_PKG_NAME");
+const DAEMON_PREFIX: &str = "/tmp";
+
+fn daemonize() -> Result<()> {
+ use std::fs;
+ use daemonize::Daemonize;
+
+ let log_file = fs::File::create(format!("{DAEMON_PREFIX}/{PKG_NAME}.log"))?;
+ let pid_file = format!("{DAEMON_PREFIX}/{PKG_NAME}.pid");
+
+ let daemonize = Daemonize::new()
+ .pid_file(&pid_file)
+ .chown_pid_file(true)
+ .working_directory(DAEMON_PREFIX)
+ .stdout(log_file);
+
+ daemonize.start()?;
+
+ // TODO: detach damonize and opt.rs and use log_println here
+ println!("start as daemon: pid {}", std::process::id());
+
+ Ok(())
+}
+
+pub fn daemonize_1() {
+ const EXIT_DAEMON_FAIL: i32 = 2;
+
+ match daemonize() {
+ Ok(_) => {},
+ Err(e) => {
+ println!("{PKG_NAME}: fail to start as daemon: {e}");
+ std::process::exit(EXIT_DAEMON_FAIL);
+ }
+ }
+}
diff --git a/src/platform/windows.rs b/src/platform/windows.rs
index 37a6654..9ad2474 100644
--- a/src/platform/windows.rs
+++ b/src/platform/windows.rs
@@ -22,6 +22,7 @@ use windivert::{
};
use std::sync::{atomic::Ordering, LazyLock, Mutex, MutexGuard};
use crate::{log::LogLevel, log_println, splash};
+use crate::opt;
fn windivert_filter() -> String {
let base = "(outbound and tcp and tcp.DstPort == 443 \
@@ -93,8 +94,10 @@ pub fn send_to_raw(pkt: &[u8]) -> Result<()> {
Ok(())
}
+use crate::RUNNING;
+
pub fn run() -> Result<()> {
- use crate::{handle_packet, RUNNING, MESSAGE_AT_RUN};
+ use crate::{handle_packet, MESSAGE_AT_RUN};
use super::PACKET_SIZE_CAP;
let mut windivert_buf = vec![0u8; 65536];
@@ -115,3 +118,44 @@ pub fn run() -> Result<()> {
Ok(())
}
+
+fn service_main() -> Result<()> {
+ let opt = opt::Opt::from_args()?;
+ opt.set_opt()?;
+ bootstrap()?;
+ run()?;
+ cleanup()?;
+
+ Ok(())
+}
+
+fn service_main_1() {
+ if service_main().is_err() {
+ std::process::exit(1);
+ }
+ std::process::exit(0);
+}
+
+pub fn daemonize_1() {
+ use windows_services::Command;
+
+ match windows_services::Service::new()
+ .can_stop()
+ .run(|_, command| {
+ match command {
+ Command::Start => {
+ std::thread::spawn(|| service_main_1());
+ }
+ Command::Stop => {
+ RUNNING.store(false, Ordering::SeqCst);
+ }
+ _ => {}
+ }
+ }) {
+ Ok(_) => {}
+ Err(e) => {
+ println!("{e}");
+ std::process::exit(1);
+ }
+ };
+}