syslog_rs/
socket.rs

1/*-
2 * syslog-rs - a syslog client translated from libc to rust
3 * 
4 * Copyright 2025 Aleksandr Morozov
5 * 
6 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
7 * the European Commission - subsequent versions of the EUPL (the "Licence").
8 * 
9 * You may not use this work except in compliance with the Licence.
10 * 
11 * You may obtain a copy of the Licence at:
12 * 
13 *    https://joinup.ec.europa.eu/software/page/eupl
14 * 
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 * Licence for the specific language governing permissions and limitations
19 * under the Licence.
20 */
21
22
23use std::fmt;
24use std::path::{PathBuf, Path};
25
26#[cfg(feature = "build_with_net")]
27use std::net::{SocketAddr, IpAddr, Ipv4Addr};
28
29#[cfg(feature = "build_with_tls")]
30use std::sync::Arc;
31
32#[cfg(feature = "build_with_tls")]
33use rustls::pki_types::CertificateDer;
34#[cfg(feature = "build_with_tls")]
35use rustls::pki_types::ServerName;
36#[cfg(feature = "build_with_tls")]
37use rustls::ClientConfig;
38#[cfg(feature = "build_with_tls")]
39use rustls::RootCertStore;
40
41use crate::formatters::SyslogFormatter;
42use crate::throw_error;
43use crate::error::SyRes; 
44
45#[cfg(feature = "build_with_net")]
46use crate::map_error;
47
48#[cfg(feature = "build_with_net")]
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50pub enum TapTypeDataRemProto
51{
52    Tcp, Udp
53}
54
55#[derive(Clone, Debug)]
56pub enum TapTypeData
57{
58    #[cfg(feature = "build_with_file")]
59    LocalFile
60    {
61        file_path: PathBuf,
62    },
63
64    Local
65    {
66        custom_remote_path: Option<PathBuf>,
67        use_alternative: bool
68    },
69
70    #[cfg(feature = "build_with_net")]
71    Remote
72    {
73        proto: TapTypeDataRemProto,
74        remote_addr: SocketAddr,
75        bind_addr: SocketAddr
76    },
77
78    #[cfg(feature = "build_with_tls")]
79    RemoteTls
80    {
81        remote_addr: SocketAddr,
82        serv_name: ServerName<'static>,
83        client_config: Arc<ClientConfig>
84    }
85}
86
87impl fmt::Display for TapTypeData
88{
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
90    {
91        match self
92        {
93            Self::Local{ .. } =>
94                write!(f, "Local Syslog Socket"),
95
96            #[cfg(feature = "build_with_file")]
97            Self::LocalFile{ .. } => 
98                write!(f, "Local File"),
99
100            #[cfg(feature = "build_with_net")]
101            Self::Remote{ .. } =>
102                write!(f, "Remote"),
103
104            #[cfg(feature = "build_with_tls")]
105            Self::RemoteTls{ .. } => 
106                write!(f, "RemoteTLS"),
107        }
108    }
109}
110
111
112impl TapTypeData
113{
114    #[inline]
115    pub(crate) 
116    fn is_same_variant(&self, other: &Self) -> bool
117    {
118        return std::mem::discriminant(self) == std::mem::discriminant(other);
119    }
120}
121
122
123
124impl TapTypeData
125{
126    #[cfg(feature = "build_with_file")]
127    pub(crate) 
128    fn new_file(path: impl Into<PathBuf>) -> SyRes<Self>
129    {
130        return Ok(
131            Self::LocalFile{ file_path: path.into() }
132        );
133    }
134
135   /* #[cfg(feature = "build_with_file")]
136    pub(crate) 
137    fn new_queue_file(path: impl Into<PathBuf>) -> SyRes<Self>
138    {
139        return Ok(
140            Self::QueueFile{ file_path: path.into() }
141        );
142    }*/
143
144    pub(crate) 
145    fn new_unix(custom_path: Option<&Path>, use_alt: bool) -> SyRes<Self>
146    {
147        let remote_path: Option<PathBuf> = 
148            if let Some(path) = custom_path
149            {
150                if use_alt == false
151                {
152                    if path.exists() == false || path.is_file() == false
153                    {
154                        throw_error!("path either does not exists or not a file: '{}'", path.display());
155                    }
156                }
157
158                Some(path.to_path_buf())
159            }
160            else
161            {
162                None
163            };
164
165        return Ok(
166            Self::Local{ custom_remote_path: remote_path, use_alternative: use_alt }
167        );
168    }
169
170    #[cfg(feature = "build_with_net")]
171    pub(crate) 
172    fn new_net<P>(proto: TapTypeDataRemProto, remote: P, local: Option<P>) -> SyRes<Self>
173    where P: AsRef<str>
174    {
175        let remote_addr: SocketAddr = 
176            remote
177                .as_ref()
178                .parse()
179                .map_err(|e| 
180                    map_error!("failed parsing remote addr '{}', error: '{}'", remote.as_ref(), e)
181                )?;
182
183        let bind_addr: SocketAddr = 
184            if let Some(bind) = local
185            {
186                bind
187                    .as_ref()
188                    .parse()
189                    .map_err(|e| 
190                        map_error!("failed parsing bind addr '{}', error: '{}'", remote.as_ref(), e)
191                    )?
192            }
193            else
194            {
195                SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0)
196            };
197
198        return Ok(
199            Self::Remote{ proto: proto, remote_addr: remote_addr, bind_addr: bind_addr }
200        );
201    }
202
203    #[cfg(feature = "build_with_tls")]
204    pub(crate) 
205    fn new_tls<P>(remote: P, server_name: &str, cert: &[u8]) -> SyRes<Self>
206    where P: AsRef<str>
207    {
208        use rustls::pki_types::pem::PemObject;
209
210        let remote_addr: SocketAddr = 
211            remote
212                .as_ref()
213                .parse()
214                .map_err(|e| 
215                    map_error!("failed parsing remote addr '{}', error: '{}'", remote.as_ref(), e)
216                )?;
217
218        let cert = 
219            CertificateDer::from_pem_slice(cert)
220                .map_err(|e|
221                    map_error!("certificate parse error: '{}'", e)
222                )?;
223
224        let mut root_store = RootCertStore::empty();
225        root_store
226            .add(cert)
227            .map_err(|e|
228                map_error!("can not add root ceritficate, error: '{}'", e)
229            )?;
230
231        let serv_name = 
232            server_name
233                .to_string()
234                .try_into()
235                .map_err(|e|
236                    map_error!("server name error: '{}'", e)
237                )?;
238
239        let client_config = 
240            rustls::ClientConfig::builder()
241                .with_root_certificates(root_store)
242                .with_no_client_auth();
243
244        return Ok(
245            Self::RemoteTls{ remote_addr: remote_addr, serv_name: serv_name, client_config: Arc::new(client_config) }
246        );
247    }
248
249
250    #[cfg(feature = "build_with_net")]
251    #[allow(unused)]
252    #[inline]
253    pub(crate) 
254    fn get_net_proto(&self) -> TapTypeDataRemProto
255    {
256        let Self::Remote { proto, .. } = self
257            else { panic!("Assertion trap: TapTypeData is not remote, misused!") };
258        
259        return *proto;
260    }
261
262    #[allow(unused)]
263    #[inline]
264    pub(crate) 
265    fn get_max_msg_length<F: SyslogFormatter>(&self) -> usize
266    {
267        use crate::common;
268
269        match self
270        {
271            TapTypeData::Local{ .. } => 
272            { 
273                if *common::RFC5424_MAX_DGRAM >= common::MAXLINE
274                {
275                    return common::MAXLINE;
276                }
277                else
278                {
279                    return *common::RFC5424_MAX_DGRAM;
280                };
281            },
282            TapTypeData::Remote{ proto, .. } => 
283            {
284                match proto
285                {
286                    TapTypeDataRemProto::Tcp => 
287                        return common::RFC5424_TCP_MAX_PKT_LEN,
288                    TapTypeDataRemProto::Udp => 
289                        return common::RFC5424_UDP_MAX_PKT_LEN,
290                }
291            },
292            TapTypeData::LocalFile{ .. } =>
293                return common::MAXLINE,
294            TapTypeData::RemoteTls{ .. } =>
295                return common::RFC5424_TCP_MAX_PKT_LEN
296        }
297    }
298}
299
300
301
302
303
304/// An enum of types of the current syslog tap instance.
305#[derive(Copy, Clone, Debug, PartialEq, Eq)]
306pub enum TapType
307{
308    /// Not defined
309    None, 
310
311    /// Unprivileged socket used
312    UnPriv,
313
314    /// Privileged socket used
315    Priv,
316
317    /// A compatibility socket used
318    OldLog,
319
320    /// Custom unix datagram
321    CustomLog,
322
323    /// Writing to file directly
324    LocalFile,
325
326    /// Network UDP
327    NetUdp,
328
329    /// Network TCP
330    NetTcp,
331
332    /// Network TLS over TCP
333    NetTls,
334}
335
336impl fmt::Display for TapType
337{
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
339    {
340        match self
341        {
342            Self::None => 
343                write!(f, "NO TYPE"),
344            Self::UnPriv => 
345                write!(f, "UNPRIV"),
346            Self::Priv => 
347                write!(f, "PRIV"),
348            Self::OldLog =>
349                write!(f, "OLD LOG"),
350            Self::CustomLog => 
351                write!(f, "CUSTOM LOG"),
352            Self::LocalFile =>
353                write!(f, "LOCAL FILE"),
354            Self::NetUdp => 
355                write!(f, "NET UDP"),
356            Self::NetTcp => 
357                write!(f, "NET TCP"),
358            Self::NetTls => 
359                write!(f, "NET TLS TCP")
360        }
361    }
362}
363
364impl TapType
365{
366    pub(crate) 
367    fn is_priv(&self) -> bool
368    {
369        return *self == Self::Priv;
370    }
371
372    pub(crate) 
373    fn is_network(&self) -> bool
374    {
375        return *self == Self::NetTcp || *self == Self::NetUdp;
376    }
377
378    pub(crate)
379    fn is_file(&self) -> bool
380    {
381        return *self == Self::LocalFile;
382    }
383
384}