//! Modulo IPC - Servidor de Named Pipes //! //! Implementa comunicacao entre o Raven UI e o Raven Service //! usando Named Pipes do Windows com protocolo JSON-RPC simplificado. use crate::{rustdesk, usb_policy}; use serde::{Deserialize, Serialize}; use std::io::{BufRead, BufReader, Write}; use thiserror::Error; use tracing::{debug, info, warn}; #[derive(Debug, Error)] pub enum IpcError { #[error("Erro de IO: {0}")] Io(#[from] std::io::Error), #[error("Erro de serializacao: {0}")] Json(#[from] serde_json::Error), } /// Requisicao JSON-RPC simplificada #[derive(Debug, Deserialize)] pub struct Request { pub id: String, pub method: String, #[serde(default)] pub params: serde_json::Value, } /// Resposta JSON-RPC simplificada #[derive(Debug, Serialize)] pub struct Response { pub id: String, #[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 ErrorResponse { pub code: i32, pub message: String, } impl Response { pub fn success(id: String, result: serde_json::Value) -> Self { Self { id, result: Some(result), error: None, } } pub fn error(id: String, code: i32, message: String) -> Self { Self { id, result: None, error: Some(ErrorResponse { code, message }), } } } /// Inicia o servidor de Named Pipes pub async fn run_server(pipe_name: &str) -> Result<(), IpcError> { info!("Iniciando servidor IPC em: {}", pipe_name); loop { match accept_connection(pipe_name).await { Ok(()) => { debug!("Conexao processada com sucesso"); } Err(e) => { warn!("Erro ao processar conexao: {}", e); } } } } /// Aceita uma conexao e processa requisicoes async fn accept_connection(pipe_name: &str) -> Result<(), IpcError> { use windows::Win32::Foundation::INVALID_HANDLE_VALUE; use windows::Win32::Security::{ InitializeSecurityDescriptor, SetSecurityDescriptorDacl, PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR, }; use windows::Win32::Storage::FileSystem::PIPE_ACCESS_DUPLEX; use windows::Win32::System::Pipes::{ ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe, PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT, }; use windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION; use windows::core::PCWSTR; // Cria o named pipe com seguranca que permite acesso a todos os usuarios let pipe_name_wide: Vec = pipe_name.encode_utf16().chain(std::iter::once(0)).collect(); // Cria security descriptor com DACL nulo (permite acesso a todos) let mut sd = SECURITY_DESCRIPTOR::default(); unsafe { let sd_ptr = PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _); let _ = InitializeSecurityDescriptor(sd_ptr, SECURITY_DESCRIPTOR_REVISION); // DACL nulo = acesso irrestrito let _ = SetSecurityDescriptorDacl(sd_ptr, true, None, false); } let sa = SECURITY_ATTRIBUTES { nLength: std::mem::size_of::() as u32, lpSecurityDescriptor: &mut sd as *mut _ as *mut _, bInheritHandle: false.into(), }; let pipe_handle = unsafe { CreateNamedPipeW( PCWSTR::from_raw(pipe_name_wide.as_ptr()), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 4096, // out buffer 4096, // in buffer 0, // default timeout Some(&sa), // seguranca permissiva ) }; // Verifica se o handle e valido if pipe_handle == INVALID_HANDLE_VALUE { return Err(IpcError::Io(std::io::Error::last_os_error())); } // Aguarda conexao de um cliente info!("Aguardando conexao de cliente..."); let connect_result = unsafe { ConnectNamedPipe(pipe_handle, None) }; if let Err(e) = connect_result { // ERROR_PIPE_CONNECTED (535) significa que o cliente ja estava conectado // o que e aceitavel let error_code = e.code().0 as u32; if error_code != 535 { warn!("Erro ao aguardar conexao: {:?}", e); } } info!("Cliente conectado"); // Processa requisicoes do cliente let result = process_client(pipe_handle); // Desconecta o cliente unsafe { let _ = DisconnectNamedPipe(pipe_handle); } result } /// Processa requisicoes de um cliente conectado fn process_client(pipe_handle: windows::Win32::Foundation::HANDLE) -> Result<(), IpcError> { use std::os::windows::io::{FromRawHandle, RawHandle}; use std::fs::File; // Cria File handle a partir do pipe let raw_handle = pipe_handle.0 as RawHandle; let file = unsafe { File::from_raw_handle(raw_handle) }; let reader = BufReader::new(file.try_clone()?); let mut writer = file; // Le linhas (cada linha e uma requisicao JSON) for line in reader.lines() { let line = match line { Ok(l) => l, Err(e) => { if e.kind() == std::io::ErrorKind::BrokenPipe { info!("Cliente desconectou"); break; } return Err(e.into()); } }; if line.is_empty() { continue; } debug!("Requisicao recebida: {}", line); // Parse da requisicao let response = match serde_json::from_str::(&line) { Ok(request) => handle_request(request), Err(e) => Response::error( "unknown".to_string(), -32700, format!("Parse error: {}", e), ), }; // Serializa e envia resposta let response_json = serde_json::to_string(&response)?; debug!("Resposta: {}", response_json); writeln!(writer, "{}", response_json)?; writer.flush()?; } // IMPORTANTE: Nao fechar o handle aqui, pois DisconnectNamedPipe precisa dele std::mem::forget(writer); Ok(()) } /// Processa uma requisicao e retorna a resposta fn handle_request(request: Request) -> Response { info!("Processando metodo: {}", request.method); match request.method.as_str() { "health_check" => handle_health_check(request.id), "apply_usb_policy" => handle_apply_usb_policy(request.id, request.params), "get_usb_policy" => handle_get_usb_policy(request.id), "provision_rustdesk" => handle_provision_rustdesk(request.id, request.params), "get_rustdesk_status" => handle_get_rustdesk_status(request.id), _ => Response::error( request.id, -32601, format!("Metodo nao encontrado: {}", request.method), ), } } // ============================================================================= // Handlers de Requisicoes // ============================================================================= fn handle_health_check(id: String) -> Response { Response::success( id, serde_json::json!({ "status": "ok", "service": "RavenService", "version": env!("CARGO_PKG_VERSION"), "timestamp": chrono::Utc::now().timestamp_millis() }), ) } fn handle_apply_usb_policy(id: String, params: serde_json::Value) -> Response { let policy = match params.get("policy").and_then(|p| p.as_str()) { Some(p) => p, None => { return Response::error(id, -32602, "Parametro 'policy' e obrigatorio".to_string()) } }; match usb_policy::apply_policy(policy) { Ok(result) => Response::success(id, serde_json::to_value(result).unwrap()), Err(e) => Response::error(id, -32000, format!("Erro ao aplicar politica: {}", e)), } } fn handle_get_usb_policy(id: String) -> Response { match usb_policy::get_current_policy() { Ok(policy) => Response::success( id, serde_json::json!({ "policy": policy }), ), Err(e) => Response::error(id, -32000, format!("Erro ao obter politica: {}", e)), } } fn handle_provision_rustdesk(id: String, params: serde_json::Value) -> Response { let config_string = params.get("config").and_then(|c| c.as_str()).map(String::from); let password = params.get("password").and_then(|p| p.as_str()).map(String::from); let machine_id = params.get("machineId").and_then(|m| m.as_str()).map(String::from); match rustdesk::ensure_rustdesk(config_string.as_deref(), password.as_deref(), machine_id.as_deref()) { Ok(result) => Response::success(id, serde_json::to_value(result).unwrap()), Err(e) => Response::error(id, -32000, format!("Erro ao provisionar RustDesk: {}", e)), } } fn handle_get_rustdesk_status(id: String) -> Response { match rustdesk::get_status() { Ok(status) => Response::success(id, serde_json::to_value(status).unwrap()), Err(e) => Response::error(id, -32000, format!("Erro ao obter status: {}", e)), } }