use anyhow::Result; use serde::{Deserialize, Serialize}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::sync::Mutex; use tracing::{info, error, warn}; use crate::client::{ClientConfig, VpnClient}; use crate::crypto; use crate::server::{ServerConfig, VpnServer}; use crate::wireguard::{self, WgClient, WgClientConfig, WgPeerConfig, WgServer, WgServerConfig}; // ============================================================================ // IPC protocol types // ============================================================================ #[derive(Debug, Deserialize)] pub struct ManagementRequest { pub id: String, pub method: String, #[serde(default)] pub params: serde_json::Value, } #[derive(Debug, Serialize)] pub struct ManagementResponse { pub id: String, pub success: bool, #[serde(skip_serializing_if = "Option::is_none")] pub result: Option, #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, } #[derive(Debug, Serialize)] pub struct ManagementEvent { pub event: String, pub data: serde_json::Value, } impl ManagementResponse { fn ok(id: String, result: serde_json::Value) -> Self { Self { id, success: true, result: Some(result), error: None, } } fn err(id: String, message: String) -> Self { Self { id, success: false, result: None, error: Some(message), } } } // ============================================================================ // Stdio management mode // ============================================================================ fn send_line_stdout(line: &str) { use std::io::Write; let stdout = std::io::stdout(); let mut handle = stdout.lock(); let _ = handle.write_all(line.as_bytes()); let _ = handle.write_all(b"\n"); let _ = handle.flush(); } fn send_response_stdout(response: &ManagementResponse) { match serde_json::to_string(response) { Ok(json) => send_line_stdout(&json), Err(e) => error!("Failed to serialize management response: {}", e), } } fn send_event_stdout(event: &str, data: serde_json::Value) { let evt = ManagementEvent { event: event.to_string(), data, }; match serde_json::to_string(&evt) { Ok(json) => send_line_stdout(&json), Err(e) => error!("Failed to serialize management event: {}", e), } } pub async fn management_loop_stdio(mode: &str) -> Result<()> { let stdin = BufReader::new(tokio::io::stdin()); let mut lines = stdin.lines(); let mut vpn_client = VpnClient::new(); let mut vpn_server = VpnServer::new(); let mut wg_client = WgClient::new(); let mut wg_server = WgServer::new(); send_event_stdout("ready", serde_json::json!({ "mode": mode })); loop { let line = match lines.next_line().await { Ok(Some(line)) => line, Ok(None) => { info!("Management stdin closed, shutting down"); break; } Err(e) => { error!("Error reading management stdin: {}", e); break; } }; let line = line.trim().to_string(); if line.is_empty() { continue; } let request: ManagementRequest = match serde_json::from_str(&line) { Ok(r) => r, Err(e) => { error!("Failed to parse management request: {}", e); send_response_stdout(&ManagementResponse::err( "unknown".to_string(), format!("Failed to parse request: {}", e), )); continue; } }; let response = match mode { "client" => handle_client_request(&request, &mut vpn_client, &mut wg_client).await, "server" => handle_server_request(&request, &mut vpn_server, &mut wg_server).await, _ => ManagementResponse::err(request.id.clone(), format!("Unknown mode: {}", mode)), }; send_response_stdout(&response); } Ok(()) } // ============================================================================ // Socket management mode // ============================================================================ pub async fn management_loop_socket(socket_path: &str, mode: &str) -> Result<()> { let _ = tokio::fs::remove_file(socket_path).await; let listener = tokio::net::UnixListener::bind(socket_path)?; info!("Management socket listening on {}", socket_path); // Shared state behind Mutex for socket mode (multiple connections) let vpn_client = std::sync::Arc::new(Mutex::new(VpnClient::new())); let vpn_server = std::sync::Arc::new(Mutex::new(VpnServer::new())); let wg_client = std::sync::Arc::new(Mutex::new(WgClient::new())); let wg_server = std::sync::Arc::new(Mutex::new(WgServer::new())); loop { match listener.accept().await { Ok((stream, _addr)) => { let mode = mode.to_string(); let client = vpn_client.clone(); let server = vpn_server.clone(); let wg_c = wg_client.clone(); let wg_s = wg_server.clone(); tokio::spawn(async move { if let Err(e) = handle_socket_connection(stream, &mode, client, server, wg_c, wg_s).await { warn!("Socket connection error: {}", e); } }); } Err(e) => { error!("Failed to accept socket connection: {}", e); } } } } async fn handle_socket_connection( stream: tokio::net::UnixStream, mode: &str, vpn_client: std::sync::Arc>, vpn_server: std::sync::Arc>, wg_client: std::sync::Arc>, wg_server: std::sync::Arc>, ) -> Result<()> { let (reader, mut writer) = stream.into_split(); let buf_reader = BufReader::new(reader); let mut lines = buf_reader.lines(); let ready_event = ManagementEvent { event: "ready".to_string(), data: serde_json::json!({ "mode": mode }), }; let ready_json = serde_json::to_string(&ready_event)?; writer.write_all(ready_json.as_bytes()).await?; writer.write_all(b"\n").await?; writer.flush().await?; loop { let line = match lines.next_line().await { Ok(Some(line)) => line, Ok(None) => { info!("Socket client disconnected"); break; } Err(e) => { error!("Error reading from socket client: {}", e); break; } }; let line = line.trim().to_string(); if line.is_empty() { continue; } let request: ManagementRequest = match serde_json::from_str(&line) { Ok(r) => r, Err(e) => { let resp = ManagementResponse::err( "unknown".to_string(), format!("Failed to parse request: {}", e), ); let json = serde_json::to_string(&resp)?; writer.write_all(json.as_bytes()).await?; writer.write_all(b"\n").await?; writer.flush().await?; continue; } }; let response = match mode { "client" => { let mut client = vpn_client.lock().await; let mut wg_c = wg_client.lock().await; handle_client_request(&request, &mut client, &mut wg_c).await } "server" => { let mut server = vpn_server.lock().await; let mut wg_s = wg_server.lock().await; handle_server_request(&request, &mut server, &mut wg_s).await } _ => ManagementResponse::err(request.id.clone(), format!("Unknown mode: {}", mode)), }; let json = serde_json::to_string(&response)?; writer.write_all(json.as_bytes()).await?; writer.write_all(b"\n").await?; writer.flush().await?; } Ok(()) } // ============================================================================ // Client command handlers // ============================================================================ async fn handle_client_request( request: &ManagementRequest, vpn_client: &mut VpnClient, wg_client: &mut WgClient, ) -> ManagementResponse { let id = request.id.clone(); match request.method.as_str() { "connect" => { // Check if transport is "wireguard" let transport = request.params .get("config") .and_then(|c| c.get("transport")) .and_then(|t| t.as_str()) .unwrap_or(""); if transport == "wireguard" { let config: WgClientConfig = match serde_json::from_value( request.params.get("config").cloned().unwrap_or_default(), ) { Ok(c) => c, Err(e) => { return ManagementResponse::err(id, format!("Invalid WG config: {}", e)); } }; match wg_client.connect(config).await { Ok(assigned_ip) => { ManagementResponse::ok(id, serde_json::json!({ "assignedIp": assigned_ip })) } Err(e) => ManagementResponse::err(id, format!("WG connect failed: {}", e)), } } else { let config: ClientConfig = match serde_json::from_value( request.params.get("config").cloned().unwrap_or_default(), ) { Ok(c) => c, Err(e) => { return ManagementResponse::err(id, format!("Invalid config: {}", e)); } }; match vpn_client.connect(config).await { Ok(assigned_ip) => { ManagementResponse::ok(id, serde_json::json!({ "assignedIp": assigned_ip })) } Err(e) => ManagementResponse::err(id, format!("Connect failed: {}", e)), } } } "disconnect" => { if wg_client.is_running() { match wg_client.disconnect().await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("WG disconnect failed: {}", e)), } } else { match vpn_client.disconnect().await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Disconnect failed: {}", e)), } } } "getStatus" => { if wg_client.is_running() { ManagementResponse::ok(id, wg_client.get_status().await) } else { let status = vpn_client.get_status().await; ManagementResponse::ok(id, status) } } "getStatistics" => { if wg_client.is_running() { ManagementResponse::ok(id, wg_client.get_statistics().await) } else { let stats = vpn_client.get_statistics().await; ManagementResponse::ok(id, stats) } } "getConnectionQuality" => { match vpn_client.get_connection_quality() { Some(quality) => { let health = vpn_client.get_link_health().await; let interval_secs = match health { crate::keepalive::LinkHealth::Healthy => 60, crate::keepalive::LinkHealth::Degraded => 30, crate::keepalive::LinkHealth::Critical => 10, }; ManagementResponse::ok(id, serde_json::json!({ "srttMs": quality.srtt_ms, "jitterMs": quality.jitter_ms, "minRttMs": quality.min_rtt_ms, "maxRttMs": quality.max_rtt_ms, "lossRatio": quality.loss_ratio, "consecutiveTimeouts": quality.consecutive_timeouts, "linkHealth": format!("{}", health), "currentKeepaliveIntervalSecs": interval_secs, })) } None => ManagementResponse::ok(id, serde_json::json!(null)), } } "getMtuInfo" => { ManagementResponse::ok(id, serde_json::json!({ "tunMtu": 1420, "effectiveMtu": crate::mtu::TunnelOverhead::default_overhead().effective_tun_mtu(1500), "linkMtu": 1500, "overheadBytes": crate::mtu::TunnelOverhead::default_overhead().total(), "oversizedPacketsDropped": 0, "icmpTooBigSent": 0, })) } _ => ManagementResponse::err(id, format!("Unknown client method: {}", request.method)), } } // ============================================================================ // Server command handlers // ============================================================================ async fn handle_server_request( request: &ManagementRequest, vpn_server: &mut VpnServer, wg_server: &mut WgServer, ) -> ManagementResponse { let id = request.id.clone(); match request.method.as_str() { "start" => { // Check if transportMode is "wireguard" let transport_mode = request.params .get("config") .and_then(|c| c.get("transportMode")) .and_then(|t| t.as_str()) .unwrap_or(""); if transport_mode == "wireguard" { let config: WgServerConfig = match serde_json::from_value( request.params.get("config").cloned().unwrap_or_default(), ) { Ok(c) => c, Err(e) => { return ManagementResponse::err(id, format!("Invalid WG config: {}", e)); } }; match wg_server.start(config).await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("WG start failed: {}", e)), } } else { let config: ServerConfig = match serde_json::from_value( request.params.get("config").cloned().unwrap_or_default(), ) { Ok(c) => c, Err(e) => { return ManagementResponse::err(id, format!("Invalid config: {}", e)); } }; match vpn_server.start(config).await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Start failed: {}", e)), } } } "stop" => { if wg_server.is_running() { match wg_server.stop().await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("WG stop failed: {}", e)), } } else { match vpn_server.stop().await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Stop failed: {}", e)), } } } "getStatus" => { if wg_server.is_running() { ManagementResponse::ok(id, wg_server.get_status()) } else { let status = vpn_server.get_status(); ManagementResponse::ok(id, status) } } "getStatistics" => { if wg_server.is_running() { ManagementResponse::ok(id, wg_server.get_statistics().await) } else { let stats = vpn_server.get_statistics().await; match serde_json::to_value(&stats) { Ok(v) => ManagementResponse::ok(id, v), Err(e) => ManagementResponse::err(id, format!("Serialize error: {}", e)), } } } "listClients" => { if wg_server.is_running() { let peers = wg_server.list_peers().await; match serde_json::to_value(&peers) { Ok(v) => ManagementResponse::ok(id, serde_json::json!({ "clients": v })), Err(e) => ManagementResponse::err(id, format!("Serialize error: {}", e)), } } else { let clients = vpn_server.list_clients().await; match serde_json::to_value(&clients) { Ok(v) => ManagementResponse::ok(id, serde_json::json!({ "clients": v })), Err(e) => ManagementResponse::err(id, format!("Serialize error: {}", e)), } } } "disconnectClient" => { let client_id = match request.params.get("clientId").and_then(|v| v.as_str()) { Some(id) => id.to_string(), None => { return ManagementResponse::err(id, "Missing clientId parameter".to_string()) } }; match vpn_server.disconnect_client(&client_id).await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Disconnect client failed: {}", e)), } } "setClientRateLimit" => { let client_id = match request.params.get("clientId").and_then(|v| v.as_str()) { Some(id) => id.to_string(), None => return ManagementResponse::err(id, "Missing clientId".to_string()), }; let rate = match request.params.get("rateBytesPerSec").and_then(|v| v.as_u64()) { Some(r) => r, None => return ManagementResponse::err(id, "Missing rateBytesPerSec".to_string()), }; let burst = match request.params.get("burstBytes").and_then(|v| v.as_u64()) { Some(b) => b, None => return ManagementResponse::err(id, "Missing burstBytes".to_string()), }; match vpn_server.set_client_rate_limit(&client_id, rate, burst).await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Failed: {}", e)), } } "removeClientRateLimit" => { let client_id = match request.params.get("clientId").and_then(|v| v.as_str()) { Some(id) => id.to_string(), None => return ManagementResponse::err(id, "Missing clientId".to_string()), }; match vpn_server.remove_client_rate_limit(&client_id).await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Failed: {}", e)), } } "getClientTelemetry" => { let client_id = match request.params.get("clientId").and_then(|v| v.as_str()) { Some(cid) => cid.to_string(), None => return ManagementResponse::err(id, "Missing clientId".to_string()), }; let clients = vpn_server.list_clients().await; match clients.into_iter().find(|c| c.client_id == client_id) { Some(info) => { match serde_json::to_value(&info) { Ok(v) => ManagementResponse::ok(id, v), Err(e) => ManagementResponse::err(id, format!("Serialize error: {}", e)), } } None => ManagementResponse::err(id, format!("Client {} not found", client_id)), } } "generateKeypair" => match crypto::generate_keypair() { Ok((public_key, private_key)) => ManagementResponse::ok( id, serde_json::json!({ "publicKey": public_key, "privateKey": private_key, }), ), Err(e) => ManagementResponse::err(id, format!("Keypair generation failed: {}", e)), }, "generateWgKeypair" => { let (public_key, private_key) = wireguard::generate_wg_keypair(); ManagementResponse::ok( id, serde_json::json!({ "publicKey": public_key, "privateKey": private_key, }), ) } "addWgPeer" => { if !wg_server.is_running() { return ManagementResponse::err(id, "WireGuard server not running".to_string()); } let config: WgPeerConfig = match serde_json::from_value( request.params.get("peer").cloned().unwrap_or_default(), ) { Ok(c) => c, Err(e) => { return ManagementResponse::err(id, format!("Invalid peer config: {}", e)); } }; match wg_server.add_peer(config).await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Add peer failed: {}", e)), } } "removeWgPeer" => { if !wg_server.is_running() { return ManagementResponse::err(id, "WireGuard server not running".to_string()); } let public_key = match request.params.get("publicKey").and_then(|v| v.as_str()) { Some(k) => k.to_string(), None => return ManagementResponse::err(id, "Missing publicKey".to_string()), }; match wg_server.remove_peer(&public_key).await { Ok(()) => ManagementResponse::ok(id, serde_json::json!({})), Err(e) => ManagementResponse::err(id, format!("Remove peer failed: {}", e)), } } "listWgPeers" => { if !wg_server.is_running() { return ManagementResponse::err(id, "WireGuard server not running".to_string()); } let peers = wg_server.list_peers().await; match serde_json::to_value(&peers) { Ok(v) => ManagementResponse::ok(id, serde_json::json!({ "peers": v })), Err(e) => ManagementResponse::err(id, format!("Serialize error: {}", e)), } } _ => ManagementResponse::err(id, format!("Unknown server method: {}", request.method)), } }