use crate::{DhcpClientManager, DhcpClientConfig};
use crate::protocol::{
ConnectionHandler, PeerCredentials, ProtocolResult, ProtocolError,
jsonrpc::{JsonRpcRequest, JsonRpcNotification},
};
use async_trait::async_trait;
use serde_json::{json, Value};
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, info};
pub struct DhcpControlHandler {
manager: Arc<RwLock<DhcpClientManager>>,
}
impl DhcpControlHandler {
pub fn new(manager: Arc<RwLock<DhcpClientManager>>) -> Self {
Self { manager }
}
async fn handle_start(&self, params: Value) -> ProtocolResult<Value> {
let interface = params
.get("interface")
.and_then(|v| v.as_str())
.ok_or_else(|| {
ProtocolError::InvalidParams("Missing or invalid 'interface' parameter".to_string())
})?;
let manager = self.manager.read().await;
manager
.start_interface(interface)
.await
.map_err(|e| {
ProtocolError::Internal(format!("Failed to start DHCP client: {}", e))
})?;
info!("Started DHCP client on interface: {}", interface);
Ok(json!({
"success": true,
"interface": interface
}))
}
async fn handle_stop(&self, params: Value) -> ProtocolResult<Value> {
let interface = params
.get("interface")
.and_then(|v| v.as_str())
.ok_or_else(|| {
ProtocolError::InvalidParams("Missing or invalid 'interface' parameter".to_string())
})?;
let manager = self.manager.read().await;
manager
.stop_interface(interface)
.await
.map_err(|e| {
ProtocolError::Internal(format!("Failed to stop DHCP client: {}", e))
})?;
info!("Stopped DHCP client on interface: {}", interface);
Ok(json!({
"success": true,
"interface": interface
}))
}
async fn handle_renew(&self, params: Value) -> ProtocolResult<Value> {
let interface = params
.get("interface")
.and_then(|v| v.as_str())
.ok_or_else(|| {
ProtocolError::InvalidParams("Missing or invalid 'interface' parameter".to_string())
})?;
let manager = self.manager.read().await;
manager
.renew_interface(interface)
.await
.map_err(|e| {
ProtocolError::Internal(format!("Failed to renew lease: {}", e))
})?;
info!("Renewed DHCP lease on interface: {}", interface);
Ok(json!({
"success": true,
"interface": interface
}))
}
async fn handle_release(&self, params: Value) -> ProtocolResult<Value> {
let interface = params
.get("interface")
.and_then(|v| v.as_str())
.ok_or_else(|| {
ProtocolError::InvalidParams("Missing or invalid 'interface' parameter".to_string())
})?;
let manager = self.manager.read().await;
manager
.release_interface(interface)
.await
.map_err(|e| {
ProtocolError::Internal(format!("Failed to release lease: {}", e))
})?;
info!("Released DHCP lease on interface: {}", interface);
Ok(json!({
"success": true,
"interface": interface
}))
}
async fn handle_status(&self, params: Option<Value>) -> ProtocolResult<Value> {
let interface_filter = params
.as_ref()
.and_then(|p| p.get("interface"))
.and_then(|v| v.as_str());
let manager = self.manager.read().await;
let mut status = manager.get_status().await;
if let Some(interface) = interface_filter {
status.v4_clients.retain(|c| c.interface == interface);
status.v6_clients.retain(|c| c.interface == interface);
}
serde_json::to_value(status).map_err(|e| {
ProtocolError::Internal(format!("Failed to serialize status: {}", e))
})
}
async fn handle_get_config(&self) -> ProtocolResult<Value> {
let manager = self.manager.read().await;
let config = manager.get_config().await;
serde_json::to_value(config).map_err(|e| {
ProtocolError::Internal(format!("Failed to serialize config: {}", e))
})
}
async fn handle_set_config(&self, params: Value) -> ProtocolResult<Value> {
let config: DhcpClientConfig = serde_json::from_value(params).map_err(|e| {
ProtocolError::InvalidParams(format!("Invalid configuration: {}", e))
})?;
let manager = self.manager.read().await;
manager.set_config(config).await.map_err(|e| {
ProtocolError::Internal(format!("Failed to set configuration: {}", e))
})?;
info!("Configuration updated");
Ok(json!({"success": true}))
}
}
#[async_trait]
impl ConnectionHandler for DhcpControlHandler {
async fn handle_request(
&self,
request: JsonRpcRequest,
credentials: PeerCredentials,
) -> ProtocolResult<Value> {
debug!(
"Handling request: {} from PID {} (UID {})",
request.method, credentials.pid, credentials.uid
);
match request.method.as_str() {
"dhcpc.start" => {
let params = request.params.ok_or_else(|| {
ProtocolError::InvalidParams("Missing parameters".to_string())
})?;
self.handle_start(params).await
}
"dhcpc.stop" => {
let params = request.params.ok_or_else(|| {
ProtocolError::InvalidParams("Missing parameters".to_string())
})?;
self.handle_stop(params).await
}
"dhcpc.renew" => {
let params = request.params.ok_or_else(|| {
ProtocolError::InvalidParams("Missing parameters".to_string())
})?;
self.handle_renew(params).await
}
"dhcpc.release" => {
let params = request.params.ok_or_else(|| {
ProtocolError::InvalidParams("Missing parameters".to_string())
})?;
self.handle_release(params).await
}
"dhcpc.status" => self.handle_status(request.params).await,
"dhcpc.getConfig" => self.handle_get_config().await,
"dhcpc.setConfig" => {
let params = request.params.ok_or_else(|| {
ProtocolError::InvalidParams("Missing parameters".to_string())
})?;
self.handle_set_config(params).await
}
"dhcpc.shutdown" => {
info!("Shutdown requested by PID {} (UID {})", credentials.pid, credentials.uid);
Ok(json!({"success": true, "message": "Shutting down"}))
}
_ => Err(ProtocolError::MethodNotFound(request.method.clone())),
}
}
async fn handle_notification(
&self,
notification: JsonRpcNotification,
credentials: PeerCredentials,
) -> ProtocolResult<()> {
debug!(
"Received notification: {} from PID {}",
notification.method, credentials.pid
);
Ok(())
}
async fn on_connect(&self, credentials: PeerCredentials) -> ProtocolResult<()> {
info!(
"Control client connected: PID {} (UID {}, GID {})",
credentials.pid, credentials.uid, credentials.gid
);
Ok(())
}
async fn on_disconnect(&self, credentials: PeerCredentials) {
info!(
"Control client disconnected: PID {} (UID {}, GID {})",
credentials.pid, credentials.uid, credentials.gid
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::DhcpClientConfig;
#[tokio::test]
async fn test_handler_creation() {
let manager = DhcpClientManager::new(DhcpClientConfig::default());
let handler = DhcpControlHandler::new(Arc::new(RwLock::new(manager)));
let request = JsonRpcRequest::new(
"dhcpc.status",
None,
json!(1),
);
let credentials = PeerCredentials {
pid: 1234,
uid: 0,
gid: 0,
};
let result = handler.handle_request(request, credentials).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_unknown_method() {
let manager = DhcpClientManager::new(DhcpClientConfig::default());
let handler = DhcpControlHandler::new(Arc::new(RwLock::new(manager)));
let request = JsonRpcRequest::new(
"dhcpc.unknown",
None,
json!(1),
);
let credentials = PeerCredentials {
pid: 1234,
uid: 0,
gid: 0,
};
let result = handler.handle_request(request, credentials).await;
assert!(result.is_err());
}
}