use async_trait::async_trait;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
use tracing::{debug, warn};
use crate::api::common::config::{SecurityInfo, SrtpProfile};
use crate::api::common::error::SecurityError;
use crate::api::server::security::SocketHandle;
use crate::api::server::security::{
ClientSecurityContext, ServerSecurityConfig, ServerSecurityContext,
};
use crate::dtls::DtlsConnection;
use crate::api::server::security::client::context::DefaultClientSecurityContext;
use crate::api::server::security::core::connection;
use crate::api::server::security::core::context;
use crate::api::server::security::dtls::transport;
use crate::api::server::security::srtp::keys;
use crate::api::server::security::util::conversion;
#[derive(Clone)]
pub struct DefaultServerSecurityContext {
config: ServerSecurityConfig,
connection_template: Arc<Mutex<Option<DtlsConnection>>>,
clients: Arc<RwLock<HashMap<SocketAddr, Arc<DefaultClientSecurityContext>>>>,
socket: Arc<Mutex<Option<SocketHandle>>>,
client_secure_callbacks:
Arc<Mutex<Vec<Box<dyn Fn(Arc<dyn ClientSecurityContext + Send + Sync>) + Send + Sync>>>>,
}
impl DefaultServerSecurityContext {
pub async fn new(
config: ServerSecurityConfig,
) -> Result<Arc<dyn ServerSecurityContext + Send + Sync>, SecurityError> {
if config.srtp_profiles.is_empty() {
return Err(SecurityError::Configuration(
"No SRTP profiles specified in server config".to_string(),
));
}
let ctx = Self {
config: config.clone(),
connection_template: Arc::new(Mutex::new(None)),
clients: Arc::new(RwLock::new(HashMap::new())),
socket: Arc::new(Mutex::new(None)),
client_secure_callbacks: Arc::new(Mutex::new(Vec::new())),
};
connection::initialize_connection_template(&config, &ctx.connection_template).await?;
Ok(Arc::new(ctx))
}
}
#[async_trait]
impl ServerSecurityContext for DefaultServerSecurityContext {
async fn initialize(&self) -> Result<(), SecurityError> {
context::initialize_security_context(
&self.config,
self.socket.lock().await.clone(),
&self.connection_template,
)
.await
}
async fn set_socket(&self, socket: SocketHandle) -> Result<(), SecurityError> {
let mut socket_lock = self.socket.lock().await;
*socket_lock = Some(socket);
Ok(())
}
async fn get_fingerprint(&self) -> Result<String, SecurityError> {
context::get_fingerprint_from_template(&self.connection_template).await
}
async fn get_fingerprint_algorithm(&self) -> Result<String, SecurityError> {
context::get_fingerprint_algorithm_from_template(&self.connection_template).await
}
async fn start_listening(&self) -> Result<(), SecurityError> {
Ok(())
}
async fn stop_listening(&self) -> Result<(), SecurityError> {
let mut clients = self.clients.write().await;
for (addr, client) in clients.iter() {
if let Err(e) = client.close().await {
warn!(
"Failed to close client security context for {}: {}",
addr, e
);
}
}
clients.clear();
Ok(())
}
async fn create_client_context(
&self,
addr: SocketAddr,
) -> Result<Arc<dyn ClientSecurityContext + Send + Sync>, SecurityError> {
{
let mut clients = self.clients.write().await;
if clients.contains_key(&addr) {
debug!(
"Client {} already exists, removing old connection for clean restart",
addr
);
clients.remove(&addr);
}
}
debug!("Creating new security context for client {}", addr);
let dtls_config = crate::dtls::DtlsConfig {
role: crate::dtls::DtlsRole::Server,
version: crate::dtls::DtlsVersion::Dtls12,
mtu: 1500,
max_retransmissions: 5,
srtp_profiles: keys::convert_profiles(&self.config.srtp_profiles),
};
let mut connection = crate::dtls::DtlsConnection::new(dtls_config);
let cert = match self.connection_template.lock().await.as_ref() {
Some(conn) => match conn.local_certificate() {
Some(cert) => cert.clone(),
None => {
return Err(SecurityError::Configuration(
"No certificate in template".to_string(),
))
}
},
None => {
return Err(SecurityError::Configuration(
"No template connection".to_string(),
))
}
};
connection.set_certificate(cert);
let socket_guard = self.socket.lock().await;
let socket = socket_guard.clone().ok_or_else(|| {
SecurityError::NotInitialized("Server socket not initialized".to_string())
})?;
drop(socket_guard);
let transport = match connection::create_dtls_transport(&socket).await {
Ok(transport) => transport,
Err(e) => return Err(e),
};
debug!("DTLS transport started for client {}", addr);
connection.set_transport(transport.clone());
let client_ctx = Arc::new(DefaultClientSecurityContext::new(
addr,
Some(connection),
Some(socket.clone()),
self.config.clone(),
Some(transport.clone()),
));
let client_ctx_clone = client_ctx.clone();
tokio::spawn(async move {
debug!("Starting handshake monitor task for client {}", addr);
match tokio::time::timeout(
std::time::Duration::from_secs(10),
client_ctx_clone.wait_for_handshake(),
)
.await
{
Ok(Ok(_)) => {
debug!(
"Server-side handshake completed successfully for client {}",
addr
);
}
Ok(Err(e)) => {
warn!("Server-side handshake failed for client {}: {}", addr, e);
}
Err(_) => {
warn!("Server-side handshake timed out for client {}", addr);
}
}
});
{
let mut clients = self.clients.write().await;
clients.insert(addr, client_ctx.clone());
}
debug!(
"Created client security context for {} - ready for handshake",
addr
);
Ok(client_ctx as Arc<dyn ClientSecurityContext + Send + Sync>)
}
async fn get_client_contexts(&self) -> Vec<Arc<dyn ClientSecurityContext + Send + Sync>> {
let clients = self.clients.read().await;
clients
.values()
.map(|c| c.clone() as Arc<dyn ClientSecurityContext + Send + Sync>)
.collect()
}
async fn remove_client(&self, addr: SocketAddr) -> Result<(), SecurityError> {
let mut clients = self.clients.write().await;
if let Some(client) = clients.remove(&addr) {
client.close().await?;
Ok(())
} else {
Ok(())
}
}
async fn on_client_secure(
&self,
callback: Box<dyn Fn(Arc<dyn ClientSecurityContext + Send + Sync>) + Send + Sync>,
) -> Result<(), SecurityError> {
let mut callbacks = self.client_secure_callbacks.lock().await;
callbacks.push(callback);
Ok(())
}
async fn get_supported_srtp_profiles(&self) -> Vec<SrtpProfile> {
self.config.srtp_profiles.clone()
}
fn is_secure(&self) -> bool {
self.config.security_mode.is_enabled()
}
fn get_security_info(&self) -> SecurityInfo {
conversion::create_security_info(
self.config.security_mode,
None, &self.config.fingerprint_algorithm,
&self.config.srtp_profiles,
)
}
async fn process_client_packet(
&self,
addr: SocketAddr,
data: &[u8],
) -> Result<(), SecurityError> {
let client_ctx = {
let clients = self.clients.read().await;
clients.get(&addr).cloned()
};
if let Some(client_ctx) = client_ctx {
debug!("Processing DTLS packet from existing client {}", addr);
client_ctx.process_dtls_packet(data).await
} else {
debug!("Creating new client context for {}", addr);
let client_ctx = self.create_client_context(addr).await?;
debug!("Starting server handshake with client {}", addr);
if let Err(e) = client_ctx.start_handshake_with_remote(addr).await {
warn!("Failed to start handshake with client {}: {}", addr, e);
return Err(e);
}
debug!("Processing initial packet from client {}", addr);
client_ctx.process_dtls_packet(data).await
}
}
async fn start_packet_handler(&self) -> Result<(), SecurityError> {
let socket_guard = self.socket.lock().await;
let socket = socket_guard.clone().ok_or_else(|| {
SecurityError::Configuration("No socket set for server security context".to_string())
})?;
drop(socket_guard);
let this = self.clone();
let handler = move |data: Vec<u8>, addr: SocketAddr| {
let this_clone = this.clone();
let data_clone = data.clone();
tokio::spawn(async move {
if let Err(e) = this_clone.process_client_packet(addr, &data_clone).await {
debug!("Error processing client packet: {:?}", e);
}
});
Ok(())
};
transport::start_packet_handler(&socket, handler).await
}
async fn capture_initial_packet(&self) -> Result<Option<(Vec<u8>, SocketAddr)>, SecurityError> {
let socket_guard = self.socket.lock().await;
let socket = socket_guard.as_ref().ok_or_else(|| {
SecurityError::NotInitialized("No socket set for server security context".to_string())
})?;
transport::capture_initial_packet(socket, 2).await
}
async fn is_ready(&self) -> Result<bool, SecurityError> {
context::is_security_context_ready(&self.socket, &self.connection_template).await
}
fn get_config(&self) -> &ServerSecurityConfig {
&self.config
}
}