use std::io;
#[cfg(unix)]
use std::os::unix::io::RawFd;
use std::sync::Arc;
pub trait SocketProtector: Send + Sync {
#[cfg(unix)]
fn protect(&self, fd: RawFd) -> io::Result<()>;
#[cfg(not(unix))]
fn protect(&self, fd: i32) -> io::Result<()>;
}
#[derive(Debug, Clone, Default)]
pub struct NoOpSocketProtector;
impl SocketProtector for NoOpSocketProtector {
#[cfg(unix)]
fn protect(&self, _fd: RawFd) -> io::Result<()> {
Ok(())
}
#[cfg(not(unix))]
fn protect(&self, _fd: i32) -> io::Result<()> {
Ok(())
}
}
pub struct FnSocketProtector<F> {
protect_fn: F,
}
impl<F> FnSocketProtector<F>
where
F: Fn(i32) -> io::Result<()> + Send + Sync,
{
pub fn new(f: F) -> Self {
Self { protect_fn: f }
}
}
impl<F> SocketProtector for FnSocketProtector<F>
where
F: Fn(i32) -> io::Result<()> + Send + Sync,
{
#[cfg(unix)]
fn protect(&self, fd: RawFd) -> io::Result<()> {
(self.protect_fn)(fd)
}
#[cfg(not(unix))]
fn protect(&self, fd: i32) -> io::Result<()> {
(self.protect_fn)(fd)
}
}
pub trait PlatformCallbacks: Send + Sync {
fn on_started(&self);
fn on_stopped(&self, error: Option<String>);
fn on_traffic_update(&self, upload_bytes: u64, download_bytes: u64);
}
#[derive(Debug, Clone, Default)]
pub struct NoOpPlatformCallbacks;
impl PlatformCallbacks for NoOpPlatformCallbacks {
fn on_started(&self) {}
fn on_stopped(&self, _error: Option<String>) {}
fn on_traffic_update(&self, _upload_bytes: u64, _download_bytes: u64) {}
}
pub struct PlatformInterface {
pub socket_protector: Arc<dyn SocketProtector>,
pub callbacks: Arc<dyn PlatformCallbacks>,
}
impl Default for PlatformInterface {
fn default() -> Self {
Self {
socket_protector: Arc::new(NoOpSocketProtector),
callbacks: Arc::new(NoOpPlatformCallbacks),
}
}
}
impl PlatformInterface {
pub fn new(
socket_protector: Arc<dyn SocketProtector>,
callbacks: Arc<dyn PlatformCallbacks>,
) -> Self {
Self {
socket_protector,
callbacks,
}
}
pub fn with_protector(socket_protector: Arc<dyn SocketProtector>) -> Self {
Self {
socket_protector,
callbacks: Arc::new(NoOpPlatformCallbacks),
}
}
}
use std::sync::RwLock;
static GLOBAL_SOCKET_PROTECTOR: RwLock<Option<Arc<dyn SocketProtector>>> = RwLock::new(None);
static PROTECTION_REQUIRED: std::sync::atomic::AtomicBool =
std::sync::atomic::AtomicBool::new(false);
pub fn set_global_socket_protector(protector: Arc<dyn SocketProtector>) {
*GLOBAL_SOCKET_PROTECTOR.write().unwrap() = Some(protector);
PROTECTION_REQUIRED.store(true, std::sync::atomic::Ordering::Release);
}
pub fn clear_global_socket_protector() {
*GLOBAL_SOCKET_PROTECTOR.write().unwrap() = None;
}
pub fn clear_global_socket_protector_if_current(protector: &Arc<dyn SocketProtector>) {
let mut current = GLOBAL_SOCKET_PROTECTOR.write().unwrap();
if current
.as_ref()
.is_some_and(|current| Arc::ptr_eq(current, protector))
{
*current = None;
}
}
pub fn get_global_socket_protector() -> Arc<dyn SocketProtector> {
GLOBAL_SOCKET_PROTECTOR
.read()
.unwrap()
.clone()
.unwrap_or_else(|| Arc::new(NoOpSocketProtector))
}
#[cfg(unix)]
pub fn protect_socket(fd: RawFd) -> io::Result<()> {
let protector = GLOBAL_SOCKET_PROTECTOR.read().unwrap().clone();
match protector {
Some(protector) => protector.protect(fd),
None if PROTECTION_REQUIRED.load(std::sync::atomic::Ordering::Acquire) => {
Err(io::Error::new(
io::ErrorKind::NotConnected,
"VPN socket protector is no longer available",
))
}
None => Ok(()),
}
}
#[cfg(not(unix))]
pub fn protect_socket(fd: i32) -> io::Result<()> {
get_global_socket_protector().protect(fd)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_noop_protector() {
let protector = NoOpSocketProtector;
assert!(protector.protect(42).is_ok());
}
#[test]
fn test_fn_protector() {
let protector = FnSocketProtector::new(|fd| {
assert_eq!(fd, 42);
Ok(())
});
assert!(protector.protect(42).is_ok());
}
#[test]
fn test_fn_protector_error() {
let protector = FnSocketProtector::new(|_fd| Err(io::Error::other("protection failed")));
assert!(protector.protect(42).is_err());
}
}