use std::fmt;
#[cfg(feature = "build_sync")]
pub(super) use crate::sync::socket::{SyslogTap, Tap};
#[cfg(feature = "async_enabled")]
pub use crate::a_sync::{AsyncTap, AsyncSyslogTap};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DestinationType
{
Local, Network, File
}
impl DestinationType
{
#[inline]
pub
fn is_network(&self) -> bool
{
return self == &Self::Network;
}
}
#[cfg(feature = "build_sync")]
pub trait SyslogDestination: fmt::Debug + fmt::Display + Send + Clone + SyslogDestMsg + 'static
{
type SocketTap: SyslogTap<Self>;
const DEST_TYPE: DestinationType;
}
#[cfg(feature = "async_enabled")]
pub trait AsyncSyslogDestination: fmt::Debug + fmt::Display + Send + Clone + SyslogDestMsg + 'static
{
type SocketTap: AsyncSyslogTap<Self>;
}
pub trait SyslogDestMsg: fmt::Debug + fmt::Display + Send + Clone + 'static
{
fn get_max_msg_len() -> usize;
}
#[cfg(target_family = "windows")]
pub mod imp_syslog_local
{
use std::ffi::CString;
use crate::WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN;
use crate::portable;
use crate::portable::EventLogLocal;
use crate::{error::SyRes, throw_error, map_error};
use super::*;
#[derive(Debug, Clone)]
pub struct WindowsEvent
{
service_name: CString,
unc_remote_server: Option<CString>,
}
impl fmt::Display for WindowsEvent
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "name: {}, guid: {}",
unsafe { str::from_utf8_unchecked(self.service_name.as_bytes()) },
self.unc_remote_server.as_ref().map_or("NONE", |f| unsafe { str::from_utf8_unchecked(f.as_bytes()) })
)
}
}
impl SyslogDestMsg for WindowsEvent
{
fn get_max_msg_len() -> usize
{
return WINDOWS_EVENT_REPORT_MAX_PAYLOAD_LEN;
}
}
impl WindowsEvent
{
pub
fn new() -> Self
{
let service_name =
portable::p_getprogname()
.map(|v|
{
if v.is_empty() == true
{
"UnknownProgram".to_string()
}
else
{
v
}
}
)
.unwrap_or("UnknownProgram".to_string());
return
Self
{
service_name:
CString::new(service_name).unwrap(),
unc_remote_server:
None,
};
}
pub
fn new_custom(service_name: &str, unc_server: Option<&str>) -> SyRes<Self>
{
let service_name_cstr =
if service_name.is_empty() == true
{
throw_error!("service_name is empty");
}
else
{
CString::new(service_name)
.map_err(|e|
map_error!("service_name CString error: '{}'", e)
)?
};
let unc_serv_cstr =
if let Some(unc) = unc_server
{
if unc.is_empty() == true
{
throw_error!("service_name is empty");
}
Some(
CString::new(unc)
.map_err(|e|
map_error!("service_name CString error: '{}'", e)
)?
)
}
else
{
None
};
return Ok(
Self
{
service_name: service_name_cstr,
unc_remote_server: unc_serv_cstr
}
);
}
#[inline]
pub
fn get_server_unc(&self) -> Option<&CString>
{
return self.unc_remote_server.as_ref();
}
#[inline]
pub
fn get_service_name(&self) -> &CString
{
return &self.service_name;
}
}
#[cfg(feature = "build_async_tokio")]
impl AsyncSyslogDestination for WindowsEvent
{
type SocketTap = AsyncTap::<EventLogLocal, Self>;
}
#[cfg(feature = "build_async_smol")]
impl AsyncSyslogDestination for WindowsEvent
{
type SocketTap = AsyncTap::<EventLogLocal, Self>;
}
#[cfg(feature = "build_sync")]
impl SyslogDestination for WindowsEvent
{
type SocketTap = Tap::<EventLogLocal, Self>;
const DEST_TYPE: DestinationType = DestinationType::Local;
}
}
#[cfg(target_family = "windows")]
pub use self::imp_syslog_local::WindowsEvent;
#[cfg(target_family = "unix")]
pub mod imp_syslog_local
{
use std::{fmt, path::{Path, PathBuf}};
use crate::{common, error::SyRes, throw_error};
use std::os::unix::net::UnixDatagram;
use super::*;
#[derive(Debug, Clone)]
pub struct SyslogLocal
{
custom_remote_path: Option<PathBuf>,
use_alternative: bool,
}
impl fmt::Display for SyslogLocal
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "custom_path: {:?}, alt: {}", self.custom_remote_path, self.use_alternative)
}
}
impl SyslogLocal
{
fn new_internal(custom_path: Option<PathBuf>, use_alt_path: bool) -> SyRes<Self>
{
let remote_path: Option<PathBuf> =
if let Some(path) = custom_path
{
if use_alt_path == false
{
if path.exists() == false || path.is_file() == false
{
throw_error!("path either does not exists or not a file: '{}'", path.display());
}
}
Some(path.to_path_buf())
}
else
{
None
};
return Ok(
Self
{
custom_remote_path: remote_path,
use_alternative: use_alt_path,
}
);
}
pub
fn new() -> Self
{
return
Self::new_internal(None, false).unwrap();
}
pub
fn new_custom_path<P: Into<PathBuf>>(custom_path: P, use_alt_path: bool) -> SyRes<Self>
{
return
Self::new_internal(Some(custom_path.into()), use_alt_path);
}
#[inline]
pub
fn get_custom_remote_path(&self) -> Option<&Path>
{
return self.custom_remote_path.as_ref().map(|f| f.as_path());
}
#[inline]
pub
fn get_use_alternative(&self) -> bool
{
return self.use_alternative;
}
}
impl SyslogDestMsg for SyslogLocal
{
fn get_max_msg_len() -> usize
{
if *common::RFC5424_MAX_DGRAM >= common::MAXLINE
{
return common::MAXLINE;
}
else
{
return *common::RFC5424_MAX_DGRAM;
};
}
}
#[cfg(feature = "build_async_tokio")]
impl AsyncSyslogDestination for SyslogLocal
{
type SocketTap = AsyncTap::<tokio::net::UnixDatagram, Self>;
}
#[cfg(feature = "build_async_smol")]
impl AsyncSyslogDestination for SyslogLocal
{
type SocketTap = AsyncTap::<smol::net::unix::UnixDatagram, Self>;
}
#[cfg(feature = "build_sync")]
impl SyslogDestination for SyslogLocal
{
type SocketTap = Tap::<UnixDatagram, Self>;
const DEST_TYPE: DestinationType = DestinationType::Local;
}
}
#[cfg(target_family = "unix")]
pub use self::imp_syslog_local::SyslogLocal;
#[cfg(feature = "build_ext_net")]
pub mod imp_syslog_net
{
use std::{fmt, net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream, UdpSocket}, time::Duration};
use crate::{common, error::SyRes, map_error, SyslogDestMsg};
use super::*;
#[derive(Debug, Clone)]
pub struct SyslogNetTcp
{
remote_addr: SocketAddr,
bind_addr: SocketAddr,
conn_timeout: Option<Duration>
}
impl fmt::Display for SyslogNetTcp
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "remote_addr: {}, bind_addr: {}, conn_timeout: {:?}", self.remote_addr,
self.bind_addr, self.conn_timeout)
}
}
impl SyslogNetTcp
{
pub
fn new<P>(remote: P, local: Option<P>, conn_timeout: Option<Duration>) -> SyRes<Self>
where P: AsRef<str>
{
let remote_addr: SocketAddr =
remote
.as_ref()
.parse()
.map_err(|e|
map_error!("failed parsing remote addr '{}', error: '{}'", remote.as_ref(), e)
)?;
let bind_addr: SocketAddr =
if let Some(bind) = local
{
bind
.as_ref()
.parse()
.map_err(|e|
map_error!("failed parsing bind addr '{}', error: '{}'", remote.as_ref(), e)
)?
}
else
{
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
};
return Ok(
Self
{
remote_addr: remote_addr,
bind_addr: bind_addr,
conn_timeout: conn_timeout
}
);
}
#[inline]
pub
fn get_remote_addr(&self) -> &SocketAddr
{
return &self.remote_addr;
}
#[inline]
pub
fn get_bind_addr(&self) -> &SocketAddr
{
return &self.bind_addr;
}
#[inline]
pub
fn get_conn_timeout(&self) -> Option<Duration>
{
return self.conn_timeout;
}
}
impl SyslogDestMsg for SyslogNetTcp
{
fn get_max_msg_len() -> usize
{
return common::RFC5424_TCP_MAX_PKT_LEN;
}
}
#[cfg(feature = "build_async_tokio")]
impl AsyncSyslogDestination for SyslogNetTcp
{
type SocketTap = AsyncTap::<tokio::net::TcpStream, Self>;
}
#[cfg(feature = "build_async_smol")]
impl AsyncSyslogDestination for SyslogNetTcp
{
type SocketTap = AsyncTap::<smol::net::TcpStream, Self>;
}
#[cfg(feature = "build_sync")]
impl SyslogDestination for SyslogNetTcp
{
type SocketTap = Tap::<TcpStream, Self>;
const DEST_TYPE: DestinationType = DestinationType::Network;
}
#[derive(Debug, Clone)]
pub struct SyslogNetUdp
{
remote_addr: SocketAddr,
bind_addr: SocketAddr
}
impl fmt::Display for SyslogNetUdp
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "remote_addr: {}, bind_addr: {}", self.remote_addr,
self.bind_addr)
}
}
impl SyslogNetUdp
{
pub
fn new<P>(dest_addr: P, local: Option<P>) -> SyRes<Self>
where P: AsRef<str>
{
let remote_addr: SocketAddr =
dest_addr
.as_ref()
.parse()
.map_err(|e|
map_error!("failed parsing remote addr '{}', error: '{}'", dest_addr.as_ref(), e)
)?;
let bind_addr: SocketAddr =
if let Some(bind) = local
{
bind
.as_ref()
.parse()
.map_err(|e|
map_error!("failed parsing bind addr '{}', error: '{}'", dest_addr.as_ref(), e)
)?
}
else
{
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
};
return Ok(
Self
{
remote_addr: remote_addr,
bind_addr: bind_addr
}
);
}
#[inline]
pub
fn get_remote_addr(&self) -> &SocketAddr
{
return &self.remote_addr;
}
#[inline]
pub
fn get_bind_addr(&self) -> &SocketAddr
{
return &self.bind_addr;
}
}
impl SyslogDestMsg for SyslogNetUdp
{
fn get_max_msg_len() -> usize
{
return common::RFC5424_UDP_MAX_PKT_LEN;
}
}
#[cfg(feature = "build_async_tokio")]
impl AsyncSyslogDestination for SyslogNetUdp
{
type SocketTap = AsyncTap::<tokio::net::UdpSocket, Self>;
}
#[cfg(feature = "build_async_smol")]
impl AsyncSyslogDestination for SyslogNetUdp
{
type SocketTap = AsyncTap::<smol::net::UdpSocket, Self>;
}
#[cfg(feature = "build_sync")]
impl SyslogDestination for SyslogNetUdp
{
type SocketTap = Tap::<UdpSocket, Self>;
const DEST_TYPE: DestinationType = DestinationType::Network;
}
}
#[cfg(feature = "build_ext_net")]
pub use self::imp_syslog_net::SyslogNetTcp;
#[cfg(feature = "build_ext_net")]
pub use self::imp_syslog_net::SyslogNetUdp;
#[cfg(feature = "build_ext_file")]
pub mod imp_syslog_file
{
use std::{fmt, fs::File, path::{Path, PathBuf}};
use crate::{common};
use super::*;
#[derive(Debug, Clone)]
pub struct SyslogFile
{
file_path: PathBuf
}
impl fmt::Display for SyslogFile
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "<file> path: {}", self.file_path.display())
}
}
impl SyslogFile
{
pub
fn new<P: Into<PathBuf>>(dest_path: P) -> Self
{
return
Self
{
file_path: dest_path.into()
};
}
#[inline]
pub
fn get_path(&self) -> &Path
{
return self.file_path.as_path();
}
}
impl SyslogDestMsg for SyslogFile
{
fn get_max_msg_len() -> usize
{
if *common::RFC5424_MAX_DGRAM >= common::MAXLINE
{
return common::MAXLINE;
}
else
{
return *common::RFC5424_MAX_DGRAM;
};
}
}
#[cfg(feature = "build_async_tokio")]
impl AsyncSyslogDestination for SyslogFile
{
type SocketTap = AsyncTap::<tokio::fs::File, Self>;
}
#[cfg(feature = "build_async_smol")]
impl AsyncSyslogDestination for SyslogFile
{
type SocketTap = AsyncTap::<smol::fs::File, Self>;
}
#[cfg(feature = "build_sync")]
impl SyslogDestination for SyslogFile
{
type SocketTap = Tap::<File, Self>;
const DEST_TYPE: DestinationType = DestinationType::File;
}
}
#[cfg(feature = "build_ext_file")]
pub use self::imp_syslog_file::SyslogFile;
#[cfg(feature = "build_ext_tls")]
pub mod imp_syslog_tls
{
use std::fmt;
use std::{net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream}, sync::Arc, time::Duration};
#[cfg(feature = "build_sync")]
use rustls::lock::Mutex;
use rustls::{pki_types::{CertificateDer, ServerName}, ClientConfig, ClientConnection, RootCertStore, StreamOwned};
use crate::{common, error::SyRes, map_error};
use super::*;
#[derive(Debug, Clone)]
pub struct SyslogTls
{
remote_addr: SocketAddr,
bind_addr: SocketAddr,
serv_name: ServerName<'static>,
client_config: Arc<ClientConfig>,
conn_timeout: Option<Duration>,
}
impl fmt::Display for SyslogTls
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "remote: {}, bind: {}, serv_name: {:?}, client: {:?}, conn_timeout: {:?}", self.remote_addr,
self.bind_addr, self.serv_name, self.client_config, self.conn_timeout)
}
}
impl SyslogTls
{
pub
fn new<P>(remote: P, local: Option<P>, serv_name: impl Into<String>,
root_cert: Vec<u8>, conn_timeout: Option<Duration>) -> SyRes<Self>
where P: AsRef<str>
{
use rustls::pki_types::pem::PemObject;
let server_name = serv_name.into();
let remote_addr: SocketAddr =
remote
.as_ref()
.parse()
.map_err(|e|
map_error!("failed parsing remote addr '{}', error: '{}'", remote.as_ref(), e)
)?;
let cert =
CertificateDer::from_pem_slice(root_cert.as_slice())
.map_err(|e|
map_error!("certificate parse error: '{}'", e)
)?;
let mut root_store = RootCertStore::empty();
root_store
.add(cert)
.map_err(|e|
map_error!("can not add root ceritficate, error: '{}'", e)
)?;
let serv_name =
server_name
.try_into()
.map_err(|e|
map_error!("server name error: '{}'", e)
)?;
let client_config =
rustls::ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
let bind_addr: SocketAddr =
if let Some(bind) = local
{
bind
.as_ref()
.parse()
.map_err(|e|
map_error!("failed parsing bind addr '{}', error: '{}'", remote.as_ref(), e)
)?
}
else
{
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
};
return Ok(
Self
{
remote_addr: remote_addr,
bind_addr: bind_addr,
serv_name: serv_name,
client_config: Arc::new(client_config),
conn_timeout: conn_timeout,
}
);
}
#[inline]
pub
fn get_remote_addr(&self) -> &SocketAddr
{
return &self.remote_addr;
}
#[inline]
pub
fn get_bind_addr(&self) -> &SocketAddr
{
return &self.bind_addr;
}
#[inline]
pub
fn get_serv_name(&self) -> ServerName<'static>
{
return self.serv_name.clone();
}
#[inline]
pub
fn get_client_config(&self) -> Arc<ClientConfig>
{
return self.client_config.clone();
}
#[inline]
pub
fn get_get_conn_timeout(&self) -> Option<Duration>
{
return self.conn_timeout.clone();
}
}
impl SyslogDestMsg for SyslogTls
{
fn get_max_msg_len() -> usize
{
return common::RFC5424_TCP_MAX_PKT_LEN;
}
}
#[cfg(feature = "build_async_tokio")]
impl AsyncSyslogDestination for SyslogTls
{
type SocketTap = AsyncTap::<tokio_rustls::client::TlsStream<tokio::net::TcpStream>, Self>;
}
#[cfg(feature = "build_async_smol")]
impl AsyncSyslogDestination for SyslogTls
{
type SocketTap = AsyncTap::<futures_rustls::client::TlsStream<smol::net::TcpStream>, Self>;
}
#[cfg(feature = "build_sync")]
impl SyslogDestination for SyslogTls
{
type SocketTap = Tap::<Mutex<StreamOwned<ClientConnection, TcpStream>>, Self>;
const DEST_TYPE: DestinationType = DestinationType::Network;
}
}
#[cfg(feature = "build_ext_tls")]
pub use self::imp_syslog_tls::SyslogTls;