- Tauri 2.0 based graphical installer - Access Key parsing with AES-256-GCM encryption - Windows Service installation via sc.exe - WinTUN driver extraction from embedded resources - System requirements checking (admin, AES-NI, OS version) - Modern dark UI with step-by-step wizard flow - Country/region selection for SNI mimicry
169 lines
4.6 KiB
Rust
169 lines
4.6 KiB
Rust
//! WinTUN driver extraction and management
|
|
//!
|
|
//! Extracts wintun.dll from embedded resources and installs the TUN adapter.
|
|
|
|
use std::path::PathBuf;
|
|
use anyhow::{Context, Result};
|
|
use std::fs;
|
|
|
|
/// Embedded wintun.dll (x64) - placeholder, actual DLL is in resources
|
|
const WINTUN_DLL: &[u8] = include_bytes!("../resources/wintun.dll");
|
|
|
|
/// WinTUN adapter GUID (consistent across installs)
|
|
#[allow(dead_code)]
|
|
const ADAPTER_GUID: &str = "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}";
|
|
|
|
/// TUN adapter name
|
|
#[allow(dead_code)]
|
|
const ADAPTER_NAME: &str = "ostp TUN";
|
|
|
|
/// Extract wintun.dll to installation directory
|
|
pub async fn extract_driver(install_path: &PathBuf) -> Result<()> {
|
|
let dll_path = install_path.join("wintun.dll");
|
|
|
|
// Create directory if needed
|
|
fs::create_dir_all(install_path)
|
|
.context("Failed to create installation directory")?;
|
|
|
|
// Check if already exists with correct version
|
|
if dll_path.exists() {
|
|
let existing = fs::read(&dll_path)?;
|
|
if existing == WINTUN_DLL {
|
|
return Ok(()); // Already up to date
|
|
}
|
|
}
|
|
|
|
// Write the DLL
|
|
fs::write(&dll_path, WINTUN_DLL)
|
|
.context("Failed to write wintun.dll")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Create the TUN adapter using wintun API
|
|
#[allow(dead_code)]
|
|
pub async fn create_adapter(install_path: &PathBuf) -> Result<()> {
|
|
// Load wintun.dll dynamically
|
|
let dll_path = install_path.join("wintun.dll");
|
|
|
|
if !dll_path.exists() {
|
|
anyhow::bail!("wintun.dll not found at {:?}", dll_path);
|
|
}
|
|
|
|
// Use libloading to call WintunCreateAdapter
|
|
// This is a simplified version - full implementation would use the wintun crate
|
|
|
|
#[cfg(windows)]
|
|
{
|
|
use std::ffi::OsStr;
|
|
use std::os::windows::ffi::OsStrExt;
|
|
|
|
// Convert strings to wide strings for Windows API
|
|
let _adapter_name: Vec<u16> = OsStr::new(ADAPTER_NAME)
|
|
.encode_wide()
|
|
.chain(std::iter::once(0))
|
|
.collect();
|
|
|
|
let _tunnel_type: Vec<u16> = OsStr::new("Ospab")
|
|
.encode_wide()
|
|
.chain(std::iter::once(0))
|
|
.collect();
|
|
|
|
// Parse GUID
|
|
let _guid = parse_guid(ADAPTER_GUID)?;
|
|
|
|
// In production, we'd call:
|
|
// WintunCreateAdapter(adapter_name, tunnel_type, &guid)
|
|
// For now, we'll use the wintun crate when it's integrated
|
|
|
|
tracing::info!("TUN adapter creation would happen here");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Delete the TUN adapter
|
|
pub async fn delete_adapter() -> Result<()> {
|
|
#[cfg(windows)]
|
|
{
|
|
// WintunDeleteAdapter would be called here
|
|
tracing::info!("TUN adapter deletion would happen here");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Remove wintun driver files
|
|
pub async fn remove_driver() -> Result<()> {
|
|
// Delete adapter first
|
|
delete_adapter().await?;
|
|
|
|
// Find and remove DLL from common locations
|
|
let paths = [
|
|
PathBuf::from(r"C:\Program Files\Ospab Network\OSTP\wintun.dll"),
|
|
];
|
|
|
|
for path in &paths {
|
|
if path.exists() {
|
|
fs::remove_file(path).ok();
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Check if wintun is installed and working
|
|
pub fn is_installed() -> bool {
|
|
let system_dll = PathBuf::from(r"C:\Program Files\Ospab Network\OSTP\wintun.dll");
|
|
system_dll.exists()
|
|
}
|
|
|
|
/// Get wintun version from DLL
|
|
#[allow(dead_code)]
|
|
pub fn get_version() -> Option<String> {
|
|
// Would extract version info from PE header
|
|
// For now, return embedded version
|
|
Some("0.14.1".to_string())
|
|
}
|
|
|
|
/// Parse a GUID string into bytes
|
|
#[cfg(windows)]
|
|
#[allow(dead_code)]
|
|
fn parse_guid(s: &str) -> Result<[u8; 16]> {
|
|
let s = s.trim_matches(|c| c == '{' || c == '}');
|
|
let parts: Vec<&str> = s.split('-').collect();
|
|
|
|
if parts.len() != 5 {
|
|
anyhow::bail!("Invalid GUID format");
|
|
}
|
|
|
|
let mut guid = [0u8; 16];
|
|
|
|
// Parse each part
|
|
let d1 = u32::from_str_radix(parts[0], 16)?;
|
|
let d2 = u16::from_str_radix(parts[1], 16)?;
|
|
let d3 = u16::from_str_radix(parts[2], 16)?;
|
|
let d4 = u16::from_str_radix(parts[3], 16)?;
|
|
let d5 = u64::from_str_radix(parts[4], 16)?;
|
|
|
|
guid[0..4].copy_from_slice(&d1.to_le_bytes());
|
|
guid[4..6].copy_from_slice(&d2.to_le_bytes());
|
|
guid[6..8].copy_from_slice(&d3.to_le_bytes());
|
|
guid[8..10].copy_from_slice(&d4.to_be_bytes());
|
|
guid[10..16].copy_from_slice(&d5.to_be_bytes()[2..8]);
|
|
|
|
Ok(guid)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
#[cfg(windows)]
|
|
fn test_parse_guid() {
|
|
let guid = parse_guid(ADAPTER_GUID).unwrap();
|
|
assert_eq!(guid.len(), 16);
|
|
}
|
|
}
|