Skip to main content

falco_event/types/primitive/
newtypes.rs

1use crate::fields::{FromBytes, FromBytesError, ToBytes};
2use std::fmt::{Debug, Formatter, LowerHex};
3
4macro_rules! default_debug {
5    ($name:ident) => {
6        impl Debug for $name {
7            fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
8                Debug::fmt(&self.0, fmt)
9            }
10        }
11    };
12}
13
14macro_rules! newtype {
15    ($(#[$attr:meta])* $name:ident($repr:ty)) => {
16        $(#[$attr])*
17        #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
18        pub struct $name(pub $repr);
19
20        impl FromBytes<'_> for $name {
21            #[inline]
22            fn from_bytes(buf: &mut &[u8]) -> Result<Self, FromBytesError>
23            where
24                Self: Sized,
25            {
26                Ok(Self(FromBytes::from_bytes(buf)?))
27            }
28        }
29
30        impl ToBytes for $name {
31            #[inline]
32            fn binary_size(&self) -> usize {
33                self.0.binary_size()
34            }
35
36            #[inline]
37            fn write<W: std::io::Write>(&self, writer: W) -> std::io::Result<()> {
38                self.0.write(writer)
39            }
40
41            #[inline]
42            fn default_repr() -> impl ToBytes {
43                <$repr>::default_repr()
44            }
45        }
46    };
47}
48
49newtype!(
50    /// Syscall result
51    SyscallResult(i64)
52);
53
54impl Debug for SyscallResult {
55    #[cfg(target_os = "linux")]
56    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
57        if self.0 < 0 {
58            let errno = nix::errno::Errno::from_raw(-self.0 as i32);
59            if errno == nix::errno::Errno::UnknownErrno {
60                // always format errors as decimal
61                write!(f, "{}", self.0)
62            } else {
63                write!(f, "{}({:?})", self.0, errno)
64            }
65        } else {
66            Debug::fmt(&self.0, f)
67        }
68    }
69
70    // not on Linux, we don't have the Linux errnos without maintaining the list ourselves
71    #[cfg(not(target_os = "linux"))]
72    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
73        if self.0 < 0 {
74            // always format errors as decimal
75            write!(f, "{}", self.0)
76        } else {
77            Debug::fmt(&self.0, f)
78        }
79    }
80}
81
82impl LowerHex for SyscallResult {
83    #[cfg(target_os = "linux")]
84    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
85        if self.0 < 0 {
86            let errno = nix::errno::Errno::from_raw(-self.0 as i32);
87            if errno == nix::errno::Errno::UnknownErrno {
88                // always format errors as decimal
89                write!(f, "{}", self.0)
90            } else {
91                write!(f, "{}({:?})", self.0, errno)
92            }
93        } else {
94            LowerHex::fmt(&self.0, f)
95        }
96    }
97
98    // not on Linux, we don't have the Linux errnos without maintaining the list ourselves
99    #[cfg(not(target_os = "linux"))]
100    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
101        if self.0 < 0 {
102            // always format errors as decimal
103            write!(f, "{}", self.0)
104        } else {
105            LowerHex::fmt(&self.0, f)
106        }
107    }
108}
109
110#[cfg(test)]
111mod syscall_result_tests {
112    use crate::types::SyscallResult;
113
114    #[test]
115    #[cfg(target_os = "linux")]
116    fn test_fmt_syscall_result() {
117        assert_eq!(format!("{:?}", SyscallResult(0)), "0");
118        assert_eq!(format!("{:?}", SyscallResult(1024)), "1024");
119        assert_eq!(format!("{:?}", SyscallResult(-2)), "-2(ENOENT)");
120        assert_eq!(format!("{:?}", SyscallResult(-28)), "-28(ENOSPC)");
121        assert_eq!(format!("{:?}", SyscallResult(-1024)), "-1024");
122
123        assert_eq!(format!("{:#x}", SyscallResult(0)), "0x0");
124        assert_eq!(format!("{:#x}", SyscallResult(1024)), "0x400");
125        assert_eq!(format!("{:#x}", SyscallResult(-2)), "-2(ENOENT)");
126        assert_eq!(format!("{:#x}", SyscallResult(-28)), "-28(ENOSPC)");
127        assert_eq!(format!("{:#x}", SyscallResult(-1024)), "-1024");
128    }
129
130    #[test]
131    #[cfg(not(target_os = "linux"))]
132    fn test_fmt_syscall_result() {
133        assert_eq!(format!("{:?}", SyscallResult(0)), "0");
134        assert_eq!(format!("{:?}", SyscallResult(1024)), "1024");
135        assert_eq!(format!("{:?}", SyscallResult(-2)), "-2");
136        assert_eq!(format!("{:?}", SyscallResult(-28)), "-28");
137        assert_eq!(format!("{:?}", SyscallResult(-1024)), "-1024");
138
139        assert_eq!(format!("{:#x}", SyscallResult(0)), "0x0");
140        assert_eq!(format!("{:#x}", SyscallResult(1024)), "0x400");
141        assert_eq!(format!("{:#x}", SyscallResult(-2)), "-2");
142        assert_eq!(format!("{:#x}", SyscallResult(-28)), "-28");
143        assert_eq!(format!("{:#x}", SyscallResult(-1024)), "-1024");
144    }
145}
146
147newtype!(
148    /// A system call number
149    SyscallId(u16)
150);
151default_debug!(SyscallId);
152
153newtype!(
154    /// A signal number
155    SigType(u8)
156);
157
158impl Debug for SigType {
159    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
160        Debug::fmt(&self.0, f)?;
161
162        #[cfg(target_os = "linux")]
163        {
164            let sig = nix::sys::signal::Signal::try_from(self.0 as i32);
165            if let Ok(sig) = sig {
166                write!(f, "({sig:?})")?;
167            }
168        }
169
170        Ok(())
171    }
172}
173
174#[cfg(test)]
175mod sig_tests {
176    use crate::types::SigType;
177
178    #[test]
179    #[cfg(target_os = "linux")]
180    fn test_sig_fmt() {
181        let formatted = format!("{:?}", SigType(1));
182        assert_eq!(formatted, "1(SIGHUP)");
183    }
184
185    #[test]
186    #[cfg(not(target_os = "linux"))]
187    fn test_sig_fmt() {
188        let formatted = format!("{:?}", SigType(1));
189        assert_eq!(formatted, "1");
190    }
191}
192
193newtype!(
194    /// File descriptor
195    Fd(i64)
196);
197
198impl Debug for Fd {
199    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
200        if self.0 == -100 {
201            f.write_str("AT_FDCWD")
202        } else {
203            Debug::fmt(&self.0, f)
204        }
205    }
206}
207
208newtype!(
209    /// File descriptor (32-bit)
210    Fd32(i32)
211);
212
213impl Debug for Fd32 {
214    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
215        if self.0 == -100 {
216            f.write_str("AT_FDCWD")
217        } else {
218            Debug::fmt(&self.0, f)
219        }
220    }
221}
222
223#[cfg(test)]
224mod fd_tests {
225    use crate::types::Fd;
226
227    #[test]
228    fn test_fd_fmt() {
229        assert_eq!(format!("{:?}", Fd(10)), "10");
230        assert_eq!(format!("{:?}", Fd(-100)), "AT_FDCWD");
231    }
232}
233
234newtype!(
235    /// Process or thread id
236    Pid(i64)
237);
238default_debug!(Pid);
239
240newtype!(
241    /// Process or thread id (32-bit)
242    Pid32(i32)
243);
244default_debug!(Pid32);
245
246newtype!(
247    /// User id
248    Uid(u32)
249);
250default_debug!(Uid);
251
252newtype!(
253    /// Group id
254    Gid(u32)
255);
256default_debug!(Gid);
257
258newtype!(
259    /// Signal set (bitmask of signals, only the lower 32 bits are used)
260    SigSet(u32)
261);
262
263impl SigSet {
264    /// Iterate over all signals in this set
265    #[inline]
266    pub fn iter(&self) -> impl Iterator<Item = SigType> + use<> {
267        let mask = self.0;
268        (0..32u8)
269            .filter(move |sig| mask & (1u32 << sig) != 0)
270            .map(SigType)
271    }
272}
273
274impl Debug for SigSet {
275    fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
276        write!(fmt, "{:#2x}", self.0)?;
277        if self.0 != 0 {
278            let mut first = true;
279            for sig in self.iter() {
280                if first {
281                    write!(fmt, "(")?;
282                    first = false;
283                } else {
284                    write!(fmt, ",")?;
285                }
286                write!(fmt, "{sig:?}")?;
287            }
288            write!(fmt, ")")?;
289        }
290
291        Ok(())
292    }
293}
294
295#[cfg(test)]
296mod sigset_tests {
297    use crate::types::SigSet;
298
299    #[test]
300    #[cfg(target_os = "linux")]
301    fn test_sigset() {
302        let signals = (1 << 2) | // SIGINT
303            (1 << 9); // SIGKILL
304
305        let formatted = format!("{:?}", SigSet(signals));
306        assert_eq!(formatted, "0x204(2(SIGINT),9(SIGKILL))");
307    }
308
309    #[test]
310    #[cfg(not(target_os = "linux"))]
311    fn test_sigset() {
312        let signals = (1 << 2) | // SIGINT
313            (1 << 9); // SIGKILL
314
315        let formatted = format!("{:?}", SigSet(signals));
316        assert_eq!(formatted, "0x204(2,9)");
317    }
318}
319
320newtype!(
321    /// IP port number
322    ///
323    /// This looks unused
324    Port(u16)
325);
326default_debug!(Port);
327
328impl LowerHex for Port {
329    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
330        Debug::fmt(&self, f)
331    }
332}
333
334#[cfg(test)]
335mod port_tests {
336    use crate::types::Port;
337
338    #[test]
339    fn test_port() {
340        assert_eq!(format!("{:?}", Port(12345)), "12345");
341        assert_eq!(format!("{:x}", Port(12345)), "12345");
342    }
343}
344
345newtype!(
346    /// Layer 4 protocol (tcp/udp)
347    ///
348    /// This looks unused
349    #[allow(unused)]
350    L4Proto(u8)
351);
352default_debug!(L4Proto);
353
354newtype!(
355    /// Socket family (`PPM_AF_*`)
356    ///
357    /// This looks unused
358    #[derive(Debug)]
359    SockFamily(u8)
360);
361
362newtype!(
363    /// Boolean value (0/1)
364    ///
365    /// This looks unused
366    Bool(u32)
367);
368
369impl Debug for Bool {
370    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
371        match self.0 {
372            0 => f.write_str("false"),
373            1 => f.write_str("true"),
374            n => write!(f, "true({n})"),
375        }
376    }
377}
378
379#[cfg(test)]
380mod bool_tests {
381    use crate::types::Bool;
382
383    #[test]
384    fn test_bool() {
385        assert_eq!(format!("{:?}", Bool(0)), "false");
386        assert_eq!(format!("{:?}", Bool(1)), "true");
387        assert_eq!(format!("{:?}", Bool(10)), "true(10)");
388    }
389}