1use crate::consts::*;
7use crate::error::{Error, Result};
8use crate::proc_event::ProcEvent;
9
10#[inline]
16fn read_u32(buf: &[u8], off: usize) -> u32 {
17 let arr: [u8; 4] = buf[off..off + 4].try_into().unwrap();
18 u32::from_ne_bytes(arr)
19}
20
21#[inline]
23fn read_u16(buf: &[u8], off: usize) -> u16 {
24 let arr: [u8; 2] = buf[off..off + 2].try_into().unwrap();
25 u16::from_ne_bytes(arr)
26}
27
28#[inline]
30fn read_i32(buf: &[u8], off: usize) -> i32 {
31 let arr: [u8; 4] = buf[off..off + 4].try_into().unwrap();
32 i32::from_ne_bytes(arr)
33}
34
35#[inline]
37fn read_u64(buf: &[u8], off: usize) -> u64 {
38 let arr: [u8; 8] = buf[off..off + 8].try_into().unwrap();
39 u64::from_ne_bytes(arr)
40}
41
42pub fn parse_netlink_message(payload: &[u8], len: usize) -> Result<Option<ProcEvent>> {
77 let payload = &payload[..len];
78
79 if payload.len() < SIZE_NLMSGHDR {
80 return Err(Error::Truncated);
81 }
82
83 let nlmsg_type = read_u16(payload, 4);
84 let nlmsg_len = read_u32(payload, 0) as usize;
85
86 if nlmsg_len > payload.len() {
87 return Err(Error::Truncated);
88 }
89
90 match nlmsg_type {
91 NLMSG_NOOP => Ok(None),
92 NLMSG_DONE if nlmsg_len == SIZE_NLMSGHDR => Ok(None),
93 NLMSG_ERROR => {
94 let errno = read_i32(payload, SIZE_NLMSGHDR);
96 if errno == 0 {
97 return Ok(None);
99 }
100 let pos_errno = errno.checked_neg().unwrap_or(errno);
103 Err(Error::Os(std::io::Error::from_raw_os_error(pos_errno)))
104 }
105 NLMSG_OVERRUN => Err(Error::Overrun),
106 _ => {
107 let cn_offset = nlmsg_hdrlen();
110 if nlmsg_len < cn_offset {
111 return Err(Error::Truncated);
112 }
113 let cn_payload = &payload[cn_offset..nlmsg_len];
114 parse_cn_msg(cn_payload).map(Some)
115 }
116 }
117}
118
119pub fn parse_cn_msg(buf: &[u8]) -> Result<ProcEvent> {
136 if buf.len() < SIZE_CN_MSG {
137 return Err(Error::Truncated);
138 }
139
140 let idx = read_u32(buf, 0);
141 let val = read_u32(buf, 4);
142
143 if idx != CN_IDX_PROC || val != CN_VAL_PROC {
145 return Err(Error::UnexpectedConnector);
146 }
147
148 let data_len = read_u16(buf, 16) as usize;
149
150 let proc_off = SIZE_CN_MSG;
152 let proc_data = if buf.len() >= proc_off + data_len {
153 &buf[proc_off..proc_off + data_len]
154 } else {
155 return Err(Error::Truncated);
156 };
157
158 parse_proc_event(proc_data)
159}
160
161fn parse_proc_event(buf: &[u8]) -> Result<ProcEvent> {
163 if buf.len() < PROC_EVENT_HEADER_SIZE {
164 return Err(Error::Truncated);
165 }
166
167 let what = read_u32(buf, 0);
168 let _cpu = read_u32(buf, 4);
169 let timestamp_ns = read_u64(buf, 8);
170
171 let data = &buf[PROC_EVENT_HEADER_SIZE..];
172
173 match what {
174 PROC_EVENT_EXEC => {
175 if data.len() < SIZE_EXEC_EVENT {
176 return Err(Error::Truncated);
177 }
178 Ok(ProcEvent::Exec {
179 pid: read_i32(data, EXEC_PID) as u32,
180 tgid: read_i32(data, EXEC_TGID) as u32,
181 timestamp_ns,
182 })
183 }
184
185 PROC_EVENT_FORK => {
186 if data.len() < SIZE_FORK_EVENT {
187 return Err(Error::Truncated);
188 }
189 Ok(ProcEvent::Fork {
190 parent_pid: read_i32(data, FORK_PARENT_PID) as u32,
191 parent_tgid: read_i32(data, FORK_PARENT_TGID) as u32,
192 child_pid: read_i32(data, FORK_CHILD_PID) as u32,
193 child_tgid: read_i32(data, FORK_CHILD_TGID) as u32,
194 timestamp_ns,
195 })
196 }
197
198 PROC_EVENT_EXIT => {
199 if data.len() < SIZE_EXIT_EVENT {
200 return Err(Error::Truncated);
201 }
202 Ok(ProcEvent::Exit {
203 pid: read_i32(data, EXIT_PID) as u32,
204 tgid: read_i32(data, EXIT_TGID) as u32,
205 exit_code: read_u32(data, EXIT_CODE),
206 exit_signal: read_u32(data, EXIT_SIGNAL),
207 timestamp_ns,
208 })
209 }
210
211 PROC_EVENT_UID => {
212 if data.len() < SIZE_ID_EVENT {
213 return Err(Error::Truncated);
214 }
215 Ok(ProcEvent::Uid {
216 pid: read_i32(data, ID_PID) as u32,
217 tgid: read_i32(data, ID_TGID) as u32,
218 ruid: read_u32(data, ID_RUID_RGID),
219 euid: read_u32(data, ID_EUID_EGID),
220 timestamp_ns,
221 })
222 }
223
224 PROC_EVENT_GID => {
225 if data.len() < SIZE_ID_EVENT {
226 return Err(Error::Truncated);
227 }
228 Ok(ProcEvent::Gid {
229 pid: read_i32(data, ID_PID) as u32,
230 tgid: read_i32(data, ID_TGID) as u32,
231 rgid: read_u32(data, ID_RUID_RGID),
232 egid: read_u32(data, ID_EUID_EGID),
233 timestamp_ns,
234 })
235 }
236
237 PROC_EVENT_SID => {
238 if data.len() < SIZE_SID_EVENT {
239 return Err(Error::Truncated);
240 }
241 Ok(ProcEvent::Sid {
242 pid: read_i32(data, SID_PID) as u32,
243 tgid: read_i32(data, SID_TGID) as u32,
244 timestamp_ns,
245 })
246 }
247
248 PROC_EVENT_PTRACE => {
249 if data.len() < SIZE_PTRACE_EVENT {
250 return Err(Error::Truncated);
251 }
252 Ok(ProcEvent::Ptrace {
253 pid: read_i32(data, PTRACE_PID) as u32,
254 tgid: read_i32(data, PTRACE_TGID) as u32,
255 tracer_pid: read_i32(data, PTRACE_TRACER_PID) as u32,
256 tracer_tgid: read_i32(data, PTRACE_TRACER_TGID) as u32,
257 timestamp_ns,
258 })
259 }
260
261 PROC_EVENT_COMM => {
262 if data.len() < SIZE_COMM_EVENT {
263 return Err(Error::Truncated);
264 }
265 let mut comm = [0u8; 16];
266 comm.copy_from_slice(&data[COMM_DATA..COMM_DATA + 16]);
267 Ok(ProcEvent::Comm {
268 pid: read_i32(data, COMM_PID) as u32,
269 tgid: read_i32(data, COMM_TGID) as u32,
270 comm,
271 timestamp_ns,
272 })
273 }
274
275 PROC_EVENT_COREDUMP => {
276 if data.len() < SIZE_COREDUMP_EVENT {
277 return Err(Error::Truncated);
278 }
279 Ok(ProcEvent::Coredump {
280 pid: read_i32(data, COREDUMP_PID) as u32,
281 tgid: read_i32(data, COREDUMP_TGID) as u32,
282 timestamp_ns,
283 })
284 }
285
286 _ => {
287 Ok(ProcEvent::Unknown {
289 what,
290 raw_data: data.to_vec(),
291 })
292 }
293 }
294}
295
296pub fn first_event_from_buf(buf: &[u8], n: usize) -> Result<Option<ProcEvent>> {
302 let iter = crate::iter::NetlinkMessageIter::new(buf, n);
303 for msg in iter {
304 match msg? {
305 Some(event) => return Ok(Some(event)),
306 None => continue,
307 }
308 }
309 Ok(None)
310}